golang - Go言語における埋め込みによるインタフェースの部分実装パターン - Qiita [キータ]golang はインタフェースがマッチしているかどうかにより処理を切り分けられる。
http://qiita.com/tenntenn/items/e04441a40aeb9c31dbaf
package main
import "fmt"
type Person struct {
FirstName string
LastName string
}
func (p *Person) Name() string {
return p.FirstName + " " + p.LastName
}
func main() {
person := &Person{"Taro", "Yamada"}
fmt.Println(person.Name())
}
メソッドを保持しているのであれば、インタフェースにアサーション出来る。
package main
import "fmt"
type Person struct {
FirstName string
LastName string
}
func (p *Person) Name() string {
return p.FirstName + " " + p.LastName
}
type Named interface {
Name() string
}
func printName(named Named) {
fmt.Println(named.Name())
}
func main() {
person := &Person{"Tarou", "Yamada"}
printName(person)
}
そして実は型がマッチしているかを確認する事も出来る。interface{}
にキャストした後に指定のインタフェースで型アサーションを行う。その際に戻り値の2つめにアサーションが成功したかどうかが返される。
package main
import "fmt"
type Person struct {
FirstName string
LastName string
}
func (p *Person) Name() string {
return p.FirstName + " " + p.LastName
}
type Named interface {
Name() string
}
// パーさんは Name メソッドを持たない
type Persan struct {
FirstName string
LastName string
}
func printName(named Named) {
fmt.Println(named.Name())
}
func main() {
person := &Person{"Tarou", "Yamada"}
named, ok := interface{}(person).(Named)
if ok {
printName(named)
} else {
fmt.Println("Person is not Named intreface")
}
persan := &Persan{"Tarou", "Yamada"}
named, ok = interface{}(persan).(Named)
if ok {
printName(named)
} else {
fmt.Println("Persan is not Named intreface")
}
}
つまり例えば、ライブラリやドライバが新しいメソッドに対応しているかどうか、と言った事を動的に確認出来る様になります。この手法はオフィシャルパッケージの
database/sql
でも使われており、各ドライバが指定のインタフェースを実装しているかどうかを確認しています。
type Execer interface {
Exec(query string, args []Value) (Result, error)
}
Exec というメソッドを持ったインタフェース Execer を用意して
if execer, ok := dc.ci.(driver.Execer); ok {
}
Execer にアサーション可能な場合だけ特別な処理を行う、という事をやっています。C/C++ の様に依存ライブラリの特定バージョン以降でメソッドがあったり無かったりという呪縛から解き放たれる訳です。
これがリフレクション無しに出来るのは便利ですね。