Go言語のインタフェースを用いたポリモーフィズム(1)


Go言語は、オブジェクト指向言語ようにクラスや継承によるクラス階層の概念を持ちませんが、インタフェースの概念は持っています。 Go言語のインタフェースはJavaのインタフェースに類似していますが、インタフェースを実現するための対象が異なります。

Java言語のインタフェースの場合は、次の例のようにインタフェースで宣言したメソッドをクラスに対して強制的に実装させることで、同じインタフェースを実装しているクラスのインスタンスに対して、インタフェースで宣言しているメソッドで同じように呼び出すことができるようになります。

interface Speaker {
	void speak();
}

class IntSpeaker implements Speaker {
	@Override
	public void speak() { ... }
}

class StringSpeaker implements Speaker {
	@Override
	public void speak() { ... }
}

class NoImplSpeaker {
	public void speak() { ... }
}
...
IntSpeaker intSpeaker = new IntSpeaker();
StringSpeaker stringSpeaker = new StringSpeaker();
NoImplSpeaker noImplSpeaker = new NoImplSpeaker();

Speaker speaker;
speaker = intSpeaker;
speaker.speak(); // OK
speaker = stringSpeaker;
speaker.speak(); // OK
speaker = noImplSpeaker; // ERROR(当然ですが、、、)
...

Go言語のインタフェースの場合は、Java言語とだいたい同じ考え方ですが、インタフェースで宣言したメソッドの実装対象が型(構造体を含む)になります。

まず、次のようにインタフェースを宣言します。インタフェース内のメソッドは複数宣言することができます。

type インタフェース名 interface {
	メソッド名(引数)戻り値
}

型にメソッドを追加する場合は、次のようにメソッドの宣言を行います。メソッド名の直前に括弧でメソッドが結びつく型を宣言します。その型のことをGo言語ではレシーバと呼びます。

func (レシーバ変数 レシーバとなる型)メソッド名(引数) 戻り値 {
...
}

ここまでの説明で、Go言語のインタフェースの宣言と型に対するメソッドの宣言について説明しましたが、型に対するメソッドの宣言は特にインタフェースを意識していないことがわかったと思います。

Go言語では、インタフェースで宣言されているメソッドがすべて型のメソッドとして宣言されている場合にのみ、インタフェースを実装していると判断します。次の例では、text型はhogeインタフェースを実装していると判断できないため、コンパイル時にエラーが発生します。

type hoge interface {
	foo()
	bar()
}

type text string
func (this text)foo() {
...
}

var h hoge
vat t text = "fugafuga"
h = t // ERROR(barメソッドが実装されていないため、hogeインタフェースを実装していると判断できない)

最後にGo言語のインタフェースを利用したポリモーフィズムの例を紹介します。

speakerインタフェースを実装したintSpeaker型とstringSpeaker型の変数をspeaker型の配列として初期化し、配列から変数を取り出してspeakメソッドをコールしています。ちなみに配列初期化時のintSpeaker(1)のような型(値)という書き方は、Go言語の変換という仕様で、Java言語やC言語のキャストと同じです。

package main

import "fmt"

type speaker interface {
	speak()
}

type intSpeaker int
func (this intSpeaker)speak() {
	fmt.Printf("%d\n", this)
}

type stringSpeaker string
func (this stringSpeaker)speak() {
	fmt.Printf("%s\n", this)
}

func main() {
	for _, item := range []speaker{intSpeaker(1), stringSpeaker("hoge")} {
		item.speak()
	}
}

あわせてお読みください