急いで学ぶGo lang#6 インターフェイス

2015.02.13

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

Goのインターフェイス

Goにはインターフェイスという、メソッドの型だけを定義した型があります。 Java等のオブジェクト指向言語を使用している人には馴染みのある単語ではないでしょうか。 これを使うとGoでもポリモーフィズムが実現できる便利な機能です。今回はインターフェイスを使ってみましょう。

動作環境

今回使用した動作環境は以下のとおりです。

  • OS : MacOS X 10.9.4
  • Go : 1.4.1
  • IDE : IntelliJ IDEA 14 CE

ここを参考に、GOPATHとGOROOTは設定しておいてください。

Goのインターフェイスを使ってみる

GoのインターフェースはJavaのインターフェースと同じくメソッドの型だけを記述した型となります。 インターフェイスはメソッドのまとまりなので、インターフェイスを通してオブジェクトの振る舞いを定義することが可能です。 Javaと違ってインターフェースで宣言されているメソッドがすべて実装されている構造体ならばどんな型でも 対象のインターフェイスとして扱うことができます。そのため、ポリモーフィズムが実現できるようになるわけです。

インターフェイスを定義する構文は下記になります。

type <型名> interface {
    メソッド名(引数の型, ...) (返り値の型, ...)
    ・
    ・
}

typeの後ろに型名記述してinterfaceキーワードを書きます。{} の中にメソッドの型を記述します。 この際、メソッド名の前にレシーバの型を書くことはできません。 では実際に定義してみましょう。 まずはCarインターフェイスを定義します。このインターフェイスはrunとstopという2つのメソッドを持っています。

//Carインターフェイスの定義
type Car interface {
	run(int) string
	stop()
}

次にMyCar構造体の定義をします。この構造体はnameとspeedというフィールドをもっています。 そしてrunとstopメソッドも実装します。これでMyCarはCarインターフェイスとして扱うことができます。

//構造体の定義
type MyCar struct {
	name  string
	speed int
}

//メソッドの実装
func (u *MyCar) run(speed int) string {
	u.speed = speed
	return strconv.Itoa(speed) + "kmで走ります"
}
func (u *MyCar) stop() {
	fmt.Println("停止します")
	u.speed = 0
}

MyCar型の変数を作成し、それをCarインターフェイス型の変数に代入しています。 なお、メソッドのレシーバがポインタ型なので、MyCarのポインタ型を使用しています。

//ポインタを返すこと
myCar := &MyCar{name: "マイカー", speed: 0}

var i Car = myCar
fmt.Println(i.run(50))
i.stop()

//実行結果
//50kmで走ります
//停止します

新しく構造体を追加した場合でも、runとstopさえ定義していれば、Car型として扱うことができます。

空のインターフェイス

空のインターフェイスには1つもメソッドを定義しません。 空インターフェイス自体は何も機能をもっていませんが、どんな型でも代入することが可能です。

// 空インターフェース定義
var x interface{}
num := 0
str := "hello"
//どんな型でも代入可能
x = num
x = str

これを利用すると、関数が空インターフェイスを返すことで任意の型の値を返すことができるようになります。

実際の型を判定する

空インターフェイスに実際に格納されている値がどの型か判断するにはどうすればいいでしょうか。 そのためにGoでは「comma ok idiom(カンマOK慣用句)」を使用します。 具体的には次のように、空インターフェイスを任意の型でチェックし、それがマッチするかどうかチェックします。

//空インターフェイス
type Element interface{}
//適当な値をセット
var element Element = "hello"

//どの型にマッチするかチェック
if value, ok := element.(int); ok {
  fmt.Println("int value:%d", value)
} else if value, ok := element.(string); ok {
  fmt.Println("string value:%d", value)
} else {
  fmt.Println("other")
}

「value, ok = element.(T)」とすると、valueは変数の値が格納され、okにはbool型が返ります。 もしelementの値が、引数の型にマッチすればokにはtrueが返されます。

また、switch文をつかった場合は次のようになります。

switch value := element.(type) {
  case int:
    fmt.Println("int value:%d", value)
  case string:
    fmt.Println("string value:%d", value)
  default:
    fmt.Println("other")
}

参考サイトなど