Goのインターフェースは、タイプがこのインターフェースに準拠するために実装しなければならないメソッドの集合です。明示的なキーワードimplementsは存在せず、互換性は_構造的_であり、宣言的ではありません。実装するタイプをインターフェース型の変数に代入することは、タイプがインターフェースのすべてのメソッドを実装している場合にのみ可能です。
重要な点: インターフェース変数は、データ(value)へのポインタと型(type)へのポインタの2つを含みます。
実装と定義の例:
type Printer interface { Print() } type MyPrinter struct{} func (mp MyPrinter) Print() { fmt.Println("printing...") } var p Printer = MyPrinter{} p.Print() // "printing..."
インターフェース変数がnilの場合、何が起こるか? "var i interface{} = nil"と"var i interface{}"の違いは何か?
よくある間違った答えは「両方の値はnil」です。実際にはそうではありません:
var i interface{} = nil — 変数は実際にnilです(type=nil, value=nil)var p *MyPrinter = nil; var i Printer = pの場合、i != nilとなります。これはtypeがnilではないからです(iの内部ではtype=*MyPrinter, value=nilであり)、if i == nilのような多くのチェックは期待通りに動作しません。例:
var p *MyPrinter = nil var i Printer = p fmt.Println(i == nil) // false!
物語
説明: あるサービスでエラーハンドラがnil値のインターフェースを返し、クライアントはエラーを非nullと見なし、過剰なアクションを呼び出しました。問題はインターフェースをnilと比較することでした。
物語
説明: テストを書く際に、nilフィールドを持つ構造体を返した後にインターフェース型のエラーがnilと等しいかどうかを誤ってチェックしました。テストは実際のエラーを検出せず、プロダクションにバグが発生することになりました。
物語
説明: あるインターフェース型から別のインターフェース型への移行中に、新しいインターフェースのすべてのメソッドを実装するのを忘れました。明示的なimplementsがなかったためです。コードはコンパイルされましたが、インターフェースは実装されず、いくつかのモック関数が動作しなくなりました。