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