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

June 18 2011, 9:59pm

今回はポリモーフィズムの例として、fmtパッケージのPrintlnメソッドを簡単に紹介したいと思います。 まず、Printlnメソッドの仕様です。

func Println(a ...interface{}) (n int, errno os.Error)

引数の宣言部分に注目して下さい。…は可変長引数を意味し、次のinterface{}が型になります。interfaceではメソッドを宣言していないため、どんな型の変数でも受け取ることが出来ます。 試しに、次のコードを実行してみて下さい。

package main

import "fmt"

func main() { i := 1234 s := "hoge" fmt.Println(i, s) }

実行結果は、次のようになります。

1234 hoge

まさに期待通りの結果になっていますが、種を明かすとfmt.Printlnは引数で渡されてくる変数が、次のインタフェースを実装されていることを期待しています。

type Stringer interface { String() string }

Printlnは受け取った値の型検証を行い、期待するインタフェースが実装されていたら、変換してインタフェースのメソッドから値を取得しています。 つまりはこんな感じです。

... s, ok := a.(Stringer) // Go言語の型アサーション if ok { result = s.String() } else { result = defaultOutput(v) } ...

変数.(型)はGo言語の型アサーションで、この例では変数aがStringer型であれば、変換した結果をsに代入し、okはtrueとなります。変換できない場合は、okはfalseとなります。

試しに、新しく構造体を定義して、その構造体の変数をPrintlnに渡してみます。

package main

import "fmt"

type value struct { name string value string }

func main() { fmt.Println(&value{name:"hoge", value:"fuga"}) // &value{name:"hoge", value:"fuga"}は、次のように書くのと同じです。 // v := new(value) // v.name = "hoge" // v.value = "fuga" }

実行結果は、次のようになります。これは期待通りなんでしょうか?!

&{hoge fuga}

期待通りの結果を得るために、次のメソッドを追加して再実行します。

func (this *value)String() string { return "value{name:\"" + this.name +"\",value:\"" + this.value + "\"}" }

実行結果は、次のようになります。バッチリですね。

value{name:"hoge",value:"fuga"}

Your favourite external commenting service goes here! I recommend http://www.disqus.com