Goにおける埋め込みは、構造体を別の構造体に埋め込むことで、明示的な継承を使用せずに構造体の挙動を「継承」することを可能にするメカニズムです。この埋め込まれた構造体は新しい構造体のフェルド(匿名フィールド)となります。
この場合、埋め込まれた構造体のすべてのメソッドは、新しい構造体のメソッドとなり、まるで「継承」されたかのようになります。
例:
package main import "fmt" type Animal struct { Name string } func (a Animal) Speak() { fmt.Println("私の名前は", a.Name) } type Dog struct { Animal // 埋め込み、匿名フィールド Breed string } func main() { d := Dog{ Animal: Animal{Name: "Rex"}, Breed: "Shepherd", } d.Speak() // 継承したメソッド! fmt.Println(d.Name) // フィールドに直接アクセス可能 }
埋め込みと通常の名前付きフィールドとしての構造体の含み方の主な違いは次のとおりです:
「埋め込まれた構造体と外部が同じ名前のフィールドを持っている場合、どのフィールドが使用され、どのようにアクセスしますか?」
多くの人は「上位のフィールドだけが使用される」と考えていますが、実際には異なります:
例:
type Base struct { Name string } type Child struct { Base // 埋め込み Name string // 一致するフィールド } c := Child{Base: Base{Name: "Base"}, Name: "Child"} fmt.Println(c.Name) // Child fmt.Println(c.Base.Name) // Base
ストーリー
プロジェクトで標準のロギング構造体を埋め込んだ後、子型に
Loggerフィールドを定義しました。その結果、d.Logger.Info()の呼び出しは期待通りに動作せず、埋め込まれたフィールドへのアクセスが同名のフィールドに埋もれてしまいました。
ストーリー
開発者は、子構造体でメソッドをオーバーライドしようとしましたが、メソッドは「オーバーライドされる」ことはなく、新しいメソッドが生成され、親メソッドは依然として
Child.Base.Method()としてアクセス可能でした。このため、コードの一部が異なるロジックを期待して「古い」バージョンのメソッドを使用することになりました。
ストーリー
埋め込まれた構造体を持つJSONのシリアル化で予期しない事態が発生しました:埋め込まれた構造体のフィールドがルートオブジェクトに現れました。埋め込みがマーシャリングに与える影響を知らなかったため、構造体は正しくシリアル化されず、APIの逆互換性が壊れました。