Go はオブジェクト指向言語ではないけど、「構造体の埋め込み」という機能によって、似たようなことができる。
例を見てみよう。構造体 Bar
にはフィールド foo
として構造体 Foo
が指定されている。これは単に、フィールドが構造体なだけであって、埋め込みとは言わない。一方、構造体 Baz にはフィールド名を省略して Foo
が指定されている。これを匿名フィールドと言って、Baz
には Foo
のフィールドやメソッドが引き継がれる。これを構造体の埋め込みという。
package main import "fmt" type Foo struct { a, b int } func (x Foo) printA() { fmt.Println("a =", x.a) } func (x Foo) printB() { fmt.Println("b =", x.b) } type Bar struct { foo Foo c int } func (x Bar) printC() { fmt.Println("c =", x.c) } type Baz struct { Foo c int } func (x Baz) printB() { fmt.Print("Baz:") x.Foo.printB() } func (x Baz) printC() { fmt.Println("c =", x.c) } func main() { x := Foo{ 1,2 } y := Bar{ Foo{ 10,20 }, 30 } z := Baz{ Foo{ 100,200 }, 300 } x.printA() x.printB() y.foo.printA() // y.printA() エラー y.foo.printB() // y.printB() エラー y.printC() z.printA() z.printB() z.printC() }
変数 y
は構造体 Bar
で、Foo
を埋め込んでいないので、y.printA()
のようなメソッド呼び出しはできない。一方、変数 z
は Foo
を埋め込んでいるので、z.printA()
のように Foo
のメソッドを呼び出すことができる。
o^ > go run struct_embed.go a = 1 b = 2 a = 10 b = 20 c = 30 a = 100 Baz:b = 200 c = 300
複数の構造体を埋め込むこともできる。ただし、埋め込んだ構造体同士でフィールド名やメソッド名が重複すると、やや面倒になる。次の例では、構造体 Baz
には Foo
と Bar
が埋め込んであるけど、フィールド a
とメソッド printA
が重複している。こういう時は構造体名を明示して呼び出さなければならない。
package main import "fmt" type Foo struct { a, b int } func (p *Foo) printA() { fmt.Println(p.a) } func (p *Foo) printB() { fmt.Println(p.b) } type Bar struct { a, c int } func (p *Bar) printA() { fmt.Println(p.a) } func (p *Bar) printC() { fmt.Println(p.c) } type Baz struct { Foo Bar } func main() { x := new(Baz) x.Bar.a = 10 x.b = 20 x.c = 30 x.Foo.printA() x.printB() x.printC() }
^o^ > go run struct_embed2.go 0 20 30