概要
Go言語のinterfaceとダックタイピングについてまとめました。
ダックタイピングとは
オブジェクト指向の話に置き換えると、
それ
が「何かしらのオブジェクト」
アヒルのように歩き
アヒルのように鳴く
が「メソッド」
に対応しています。
先ほどの文章を言い換えると、
もしもオブジェクトがDuckオブジェクトのようにWalk()メソッドを呼び出すことができ、DuckオブジェクトのようにSound()メソッドを呼び出すことができるのなら、それはDuckオブジェクトとして扱える。
となるでしょうか。
Rubyでのダックタイピング
受け取る引数fooの型が何かは制限していません。 soundメソッドを呼び出すことができれば問題がありません。
def test(foo) puts foo.sound end
これの何が嬉しいか。
例えば、テストを書く時にモックオブジェクトを渡せます。
以下は、ダックタイピングではないJavaのコードです。
受け取る引数fooの型をDuckに制限しています。
void test(Duck foo) {
foo.sound();
}
Javaでinterfaceを使ったダックタイピング(のようなもの)
Javaでもinterfaceを使ってダックタイピングの同じようなことができます。
interface ISomething { void sound(); } void test(ISomething foo) { foo.sound(); }
このコードでも、引数ではSomethingを実装(soundメソッドを実装)したオブジェクトであれば、受け取れるようになっています。直接Duckオブジェクトを受け取る形より結合度は低いですね。
例えば、testメソッドにDuckMockオブジェクトを渡したい場合は以下のように定義します。
class DuckMock implements ISomething { void sound() { system.out.println("Ga-Ga-"); } }
DuckMockはISomethingを実装しているということを明示的に宣言しています。
Goのダックタイピング
Go言語にもinterfaceがあり、以下のように書けます。
type Something interface { Sound() }
Javaのインターフェイスと異なる点は、implementsで明示的に宣言しなくても良いところです。DuckMockは以下のように書きます。
type DuckMock struct {} func (d *DuckMock) Sound() { fmt.Println("Ga-Ga-") }
単純にインターフェースと同じシグニチャーを持ったメソッドを定義するだけで済みます。
この点で、よりダックタイピングに近い形になっているので、Go言語のinterfaceの機能は型付きのダックタイピングと言えます。
おまけ:一つもメソッドがないinterfaceは、どういった型を受け取るか?
以下のfooはどういった型を受け取るでしょうか?
func test(foo interface{}) { ... }
答えは「どんな型でも受け取る」です。一つもメソッドが指定されていないので、どのオブジェクトでも当てはまるということですね。
Go言語では、色々な型を受け取りたい場合は、これを応用します。極力避けたほうが良いですが。
まとめ
Go言語のinterfaceをダックタイピングという概念とともに解説してみました。他のオブジェクト指向をやってきた人でも戸惑うポイントだと思うので、理解の助けになれば幸いです。