問題の歴史:
Goは、ダックタイピングに基づいたシンプルでありながら強力なメソッドとインターフェースの概念を実装しています。メソッドは名前付き型(構造体またはエイリアス)のみにバインドでき、インターフェースはメソッドの集合です。これは、implementsを明示的に指定することなく、ポリモーフィズムと抽象化の鍵となります。
問題点:
JavaやC#から来たプログラマーは、しばしば明示的なキーワードであるimplementsやextendsを期待し、値渡しによるレシーバとポインタによるレシーバの違いに混乱し、インターフェースの実装時に予測できない動作やエラーを引き起こします。
解決策:
メソッドとインターフェースの定義:
type User struct { Name string } func (u User) Greet() string { return "Hello, " + u.Name } func (u *User) SetName(name string) { u.Name = name } type Greeter interface { Greet() string }
主な特長:
intやstringの基本型に対して型エイリアスのメソッドを宣言できますか?
はい、名前付き型であれば可能です。例えば:
type MyInt int func (m MyInt) Double() int { return int(m) * 2 }
ポインタによるメソッドと値によるメソッドでインターフェースの実装はどう異なりますか?
インターフェースのメソッドが(T)として宣言されている場合、両方のTと*Tがインターフェースを実装します。(*T)の場合は、*Tのみがインターフェースを満たします。これは、インターフェースを受け取る関数に構造体を渡す際に重要です。
インターフェースの「ゼロ値」とはどのようなもので、nil値でメソッドを呼び出すとどうなりますか?
未初期化のインターフェース変数はnilに等しいです。nilポインタを処理する明示的な処理が実装されていない場合、nilフィールド上でメソッドを呼び出すとパニックになります。
型がポインタメソッド(*T)で宣言されていて、インターフェースが値メソッド(T)を期待している場合、インターフェースで使用しようとすると構造体がコンパイルされません。型Userではなく、[]User型にメソッドを宣言しようとする。
長所:
短所:
インターフェースを実装するすべてのメソッドが適切なレシーバで宣言されている。不要な場所でインターフェースが使用されていない(可能な場合は具象型を使用し、必要な場合はポリモーフィズムを使用する)。
長所:
短所: