構造体の埋め込み

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() のようなメソッド呼び出しはできない。一方、変数 zFoo を埋め込んでいるので、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 には FooBar が埋め込んであるけど、フィールド 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