跳转到主要内容
模块 5 · 第 3 课(共 5)课程中的23/50~12 min
模块课程(3/5)

组合(嵌入)

Go 没有继承。重用的惯用答案是 组合,特别是嵌入:包括一种类型 在另一个中而不给它一个字段名称。领域和方法 嵌入类型得到提升,变得可以直接访问,就像 它们属于外型。

结构嵌入

Go
type Animal struct {
    Name string
}

func (a Animal) Greet() string {
    return "ciao da " + a.Name
}

type Dog struct {
    Animal        // embedding — NO field name
    Breed string
}

c := Dog{
    Animal: Animal{Name: "Rex"},
    Breed:  "Pastore",
}

fmt.Println(c.Name)         // "Rex" — promoted from Animal
fmt.Println(c.Greet())      // "ciao da Rex" — promoted method
fmt.Println(c.Breed)        // own field

The embedded field is still accessible by the type's name: c.Animal.Name works and is identical to c.Name.当需要时 有碰撞。

覆盖(部分):方法“shadowing”

If the outer type defines a method with the same name as the promoted 一、其方法“胜”。从内部您始终可以到达嵌入的 一种通过类型名称:

Go
func (c Dog) Greet() string {
    return "BAU! " + c.Animal.Greet()
}

它不是多态继承:它只是词法名称解析。 对于多态性,您可以使用接口(模块 6)。

多重嵌入

您可以嵌入多种类型:

Go
type Logger struct{ /*...*/ }
func (l Logger) Log(msg string) { /*...*/ }

type Timer struct{ /*...*/ }
func (t Timer) Now() time.Time { /*...*/ }

type Service struct {
    Logger
    Timer
    Name string
}

s := Service{Name: "api"}
s.Log("start")   // promoted from Logger
s.Now()          // promoted from Timer

如果两个嵌入类型具有相同名称的方法,则直接访问 变得不明确,编译器需要显式形式 (s.Logger.Foo() 或 s.Timer.Foo())。

接口嵌入

接口也可以“嵌入”(参见模块 6):

Go
type ReadWriter interface {
    io.Reader
    io.Writer
}

它相当于 Reader 和 Writer 方法的联合。

何时使用显式组合(而不是嵌入)

If you want a named field and no auto-promotion, use plain 组成:

Go
type Dog struct {
    Anim  Animal    // NORMAL field, no promotion
    Breed string
}
c.Anim.Name   // you need the field name

Embedding is a handy delegation tool, not a design choice strictly better than explicit composition: use it when you really want 提升内部类型的API。

尝试一下

锻炼#go.m5.l3.e1
尝试:0加载中...

定义 Dog,嵌入 Animal(嵌入,无字段名称)并具有 Breed 字符串字段。

正在加载编辑器...
显示提示

嵌入:编写类型而不给出字段名称。

3 次尝试后可用的解决方案

锻炼#go.m5.l3.e2
尝试:0加载中...

向 Animal 添加一个 Greet() 字符串方法,并直接在 Dog 变量上调用它(方法升级)。

正在加载编辑器...
显示提示

由于嵌入,Animal 上定义的方法被提升为 Dog。

3 次尝试后可用的解决方案

Quiz#go.m5.l3.e3
准备好

Go 支持经典继承吗?

Go
type B struct { /* extends A? */ }
答案选项

回顾

  • 无继承:您使用嵌入进行组合。
  • 嵌入 = 在结构中写入不带字段名称的 TYPE。
  • 嵌入类型的字段和方法得到提升。
  • 显式访问始终可用:outer.Inner.Field。
  • “词法”覆盖:外部类型的方法隐藏了升级的方法。
  • 嵌入≠ is-a:对于多态性,使用接口。