問題の歴史:
Goでは、組み込みタイプが不十分である場合が多く、ロジックのカプセル化と機能の拡張のために独自のデータ型を定義する必要があります。これは、カスタムタイプ(type)とメソッド(func (r Receiver) MethodName())を作成することで達成されます。
問題:
初学者の開発者は、新しいタイプを既存のものに基づいて宣言することの違いに混乱することがよくあります。メソッドを正しく実装するにはどうすれば良いのでしょうか? コピーや値/ポインタの渡し方はどのように解決されるのでしょうか? スコープ、レシーバータイプ、および埋め込み構造体の操作で間違えることがよくあります。
解決策:
独自のタイプを定義するには、typeというキーワードを使用します。メソッドは、インターフェースとの互換性を持たせるためにレシーバーを利用して実装されます。
コード例:
type MyInt int func (m MyInt) Double() int { return int(m) * 2 } // 構造体の場合: type User struct { Name string Age int } func (u *User) Birthday() { u.Age++ } var u = User{"Alice", 30} u.Birthday() // Age = 31
主な特徴:
カスタムタイプは基本タイプのメソッドを継承しますか?
いいえ。type MyInt intを定義すると、MyIntはintのメソッドを持ちません。例えば、String()や他の基本タイプのメソッドを呼び出すことはできません。
タイプエイリアスにメソッドを定義できますか?
エイリアス(type MyType = ExistingType)にはメソッドを追加できません。メソッドは新しいタイプ(type MyType ExistingType)のみで定義され、エイリアスには適用されません。
どのレシーバーを使用すべきか:ポインタまたは値?
もしメソッドがオブジェクトを変更する必要がある場合は、ポインタを使用する方が良いです。値レシーバーは構造体をコピーするため、例えば構造体がスライスやマップのフィールドを含む場合に予期しない動作を引き起こす可能性があります。
コード例:
type Counter struct { value int } func (c *Counter) Inc() { c.value++ } func main() { c := Counter{} c.Inc() // ポインタを使わない場合、メソッドはvalueを変更しません }
プログラマーはtype MySlice []intを作成し、[]intのメソッド、例えばappendがMySliceタイプで機能することを期待していました。結果、メソッドは無く、MySliceを[]intとして直接参照することはできないことが判明しました。
利点:
欠点:
type Counter intが定義され、Incメソッドが実装されました。これにより、プログラムの複数の部分で共通のロジックを再利用でき、コードの繰り返しを避けました。
利点:
欠点: