# 类型内嵌

从结构体一文中，我们得知一个结构体类型可以拥有若干字段。 每个字段由一个字段名和一个字段类型组成。事实上，有时，一个字段可以仅由一个字段类型组成。 这样的字段声明方式称为类型内嵌（type embedding）。

此篇文章将解释类型内嵌的目的和各种和类型内嵌相关的细节。

### 类型内嵌语法

下面是一个使用了类型内嵌的例子：

    package main

    import "net/http"

    func main() {
    	type P = *bool
    	type M = map[int]int
    	var x struct {
    		string // 一个具名非指针类型
    		error  // 一个具名接口类型
    		*int   // 一个无名指针类型
    		P      // 一个无名指针类型的别名
    		M      // 一个无名类型的别名

    		http.Header // 一个具名映射类型
    	}
    	x.string = "Go"
    	x.error = nil
    	x.int = new(int)
    	x.P = new(bool)
    	x.M = make(M)
    	x.Header = http.Header{}
    }

在上面这个例子中，有六个类型被内嵌在了一个结构体类型中。每个类型内嵌形成了一个内嵌字段（embedded field）。

因为历史原因，内嵌字段有时也称为匿名字段。但是，事实上，每个内嵌字段有一个（隐式的）名字。 此字段的非限定（unqualified）类型名即为此字段的名称。 比如，上例中的六个内嵌字段的名称分别为`string`、`error`、`int`、`P`、`M`和`Header`。

### 哪些类型可以被内嵌？

当前的Go白皮书（1.23）规定：

> An embedded field must be specified as a type name `T` or as a pointer to a non-interface type name `*T`, and `T` itself may not be a pointer type.

翻译过来：

> 一个内嵌字段必须被声明为形式`T`或者一个基类型为非接口类型的指针类型`*T`，其中`T`为一个类型名但是`T`不能表示一个指针类型。

此规则描述在Go 1.9之前是精确的。但是随着从Go 1.9引入的自定义类型别名概念，此描述有些过时和不太准确了。 比如，此描述没有包括上一节的例子中的`P`内嵌字段的情形。

这里，本文试图使用一个更精确的描述：

- 一个类型名`T`只有在它既不表示一个具名指针类型也不表示一个基类型为指针类型或者接口类型的指针类型的情况下才可以被用做内嵌字段。
- 一个指针类型`*T`只有在`T`为一个类型名并且`T`既不表示一个指针类型也不表示一个接口类型的时候才能被用做内嵌字段。

下面列出了一些可以被或不可以被内嵌的类型或别名：

    type Encoder interface {Encode([]byte) []byte}
    type Person struct {name string; age int}
    type Alias = struct {name string; age int}
    type AliasPtr = *struct {name string; age int}
    type IntPtr *int
    type AliasPP = *IntPtr

    // 这些类型或别名都可以被内嵌。
    Encoder
    Person
    *Person
    Alias
    *Alias
    AliasPtr
    int
    *int

    // 这些类型或别名都不能被内嵌。
    AliasPP          // 基类型为一个指针类型
    *Encoder         // 基类型为一个接口类型
    *AliasPtr        // 基类型为一个指针类型
    IntPtr           // 具名指针类型
    *IntPtr          // 基类型为一个指针类型
    *chan int        // 基类型为一个无名类型
    struct {age int} // 无名非指针类型
    map[string]int   // 无名非指针类型
    []int64          // 无名非指针类型
    func()           // 无名非指针类型

一个结构体类型中不允许有两个同名字段，此规则对匿名字段同样适用。 根据上述内嵌字段的隐含名称规则，一个无名指针类型不能和它的基类型同时内嵌在同一个结构体类型中。 比如，`int`和`*int`类型不能同时内嵌在同一个结构体类型中。

一个结构体类型不能内嵌（无论间接还是直接）它自己。

一般说来，只有内嵌含有字段或者拥有方法的类型才有意义（后续几节将阐述原因），尽管很多既没有字段也没有方法的类型也可以被内嵌。

### 类型内嵌的意义是什么？

类型内嵌的主要目的是为了将被内嵌类型的功能扩展到内嵌它的结构体类型中，从而我们不必再为此结构体类型重复实现被内嵌类型的功能。

很多其它流行面向对象的编程语言都是用继承来实现上述目的。两种实现方式有它们各自的利弊。 这里，此篇文章将不讨论哪种方式更好一些，我们只需知道Go选择了类型内嵌这种方式。 这两种方式有一个很大的不同点：

- 如果类型`T`继承了另外一个类型，则类型`T`获取了另外一个类型的能力。 同时，一个`T`类型的值也可以被当作另外一个类型的值来使用。
- 如果一个类型`T`内嵌了另外一个类型，则另外一个类型变成了类型`T`的一部分。 类型`T`获取了另外一个类型的能力，但是`T`类型的任何值都不能被当作另外一个类型的值来使用。

下面是一个展示了如何通过类型内嵌来扩展类型功能的例子：

    package main

    import "fmt"

    type Person struct {
    	Name string
    	Age  int
    }
    func (p Person) PrintName() {
    	fmt.Println("Name:", p.Name)
    }
    func (p *Person) SetAge(age int) {
    	p.Age = age
    }

    type Singer struct {
    	Person // 通过内嵌Person类型来扩展之
    	works  []string
    }

    func main() {
    	var gaga = Singer{Person: Person{"Gaga", 30}}
    	gaga.PrintName() // Name: Gaga
    	gaga.Name = "Lady Gaga"
    	(&gaga).SetAge(31)
    	(&gaga).PrintName()   // Name: Lady Gaga
    	fmt.Println(gaga.Age) // 31
    }

从上例中，当类型`Singer`内嵌了类型`Person`之后，看上去类型`Singer`获取了类型`Person`所有的字段和方法， 并且类型`*Singer`获取了类型`*Person`所有的方法。此结论是否正确？随后几节将给出答案。

注意，类型`Singer`的一个值不能被当作`Person`类型的值用。下面的代码编译不通过：

    var gaga = Singer{}
    var _ Person = gaga

### 当一个结构体类型内嵌了另一个类型，此结构体类型是否获取了被内嵌类型的字段和方法？

下面这个程序使用反射列出了上一节的例子中的`Singer`类型的字段和方法，以及`*Singer`类型的方法。

    package main

    import (
    	"fmt"
    	"reflect"
    )

    ... // 为节省篇幅，上一个例子中声明的类型在这里省略了。

    func main() {
    	t := reflect.TypeOf(Singer{}) // the Singer type
    	fmt.Println(t, "has", t.NumField(), "fields:")
    	for i := 0; i < t.NumField(); i++ {
    		fmt.Print(" field#", i, ": ", t.Field(i).Name, "\n")
    	}
    	fmt.Println(t, "has", t.NumMethod(), "methods:")
    	for i := 0; i < t.NumMethod(); i++ {
    		fmt.Print(" method#", i, ": ", t.Method(i).Name, "\n")
    	}

    	pt := reflect.TypeOf(&Singer{}) // the *Singer type
    	fmt.Println(pt, "has", pt.NumMethod(), "methods:")
    	for i := 0; i < pt.NumMethod(); i++ {
    		fmt.Print(" method#", i, ": ", pt.Method(i).Name, "\n")
    	}
    }

输出结果：

    main.Singer has 2 fields:
     field#0: Person
     field#1: works
    main.Singer has 1 methods:
     method#0: PrintName
    *main.Singer has 2 methods:
     method#0: PrintName
     method#1: SetAge

从此输出结果中，我们可以看出类型`Singer`确实拥有一个`PrintName`方法，以及类型`*Singer`确实拥有两个方法：`PrintName`和`SetAge`。 但是类型`Singer`并不拥有一个`Name`字段。那么为什么选择器表达式`gaga.Name`是合法的呢？ 毕竟`gaga`是`Singer`类型的一个值。 请阅读下一节以获取原因。

### 选择器的缩写形式

从前面的结构体和方法两篇文章中，我们得知，对于一个值`x`，`x.y`称为一个选择器，其中`y`可以是一个字段名或者方法名。 如果`y`是一个字段名，那么`x`必须为一个结构体值或者结构体指针值。 一个选择器是一个表达式，它表示着一个值。 如果选择器`x.y`表示一个字段，此字段也可能拥有自己的字段（如果此字段的类型为另一个结构体类型）和方法，比如`x.y.z`，其中`z`可以是一个字段名，也可是一个方法名。

在Go中，（不考虑下面将要介绍的选择器碰撞和遮挡），如果一个选择器中的中部某项对应着一个内嵌字段，则此项可被省略掉。 因此内嵌字段又被称为匿名字段。

一个例子：

    package main

    type A struct {
    	x int
    }

    func (a A) MethodA() {}

    type B struct {
    	*A
    }

    type C struct {
    	B
    }

    func main() {
    	var c = &C{B: B{A: &A{FieldX: 5}}}

    	// 这几行是等价的。
    	_ = c.B.A.FieldX
    	_ = c.B.FieldX
    	_ = c.A.FieldX // A是类型C的一个提升字段
    	_ = c.FieldX   // FieldX也是一个提升字段

    	// 这几行是等价的。
    	c.B.A.MethodA()
    	c.B.MethodA()
    	c.A.MethodA()
    	c.MethodA() // MethodA是类型C的一个提升方法
    }

这就是为什么在上一节的例子中选择器表达式`gaga.Name`是合法的， 因为它只不过是`gaga.Person.Name`的一个缩写形式。

类似的，选择器`gaga.PrintName`可以被看作是`gaga.Person.PrintName`的缩写形式。 但是，我们也可以不把它看作是一个缩写。毕竟，类型`Singer`确实拥有一个`PrintName`方法， 尽管此方法是被隐式声明的（请阅读下下节以获得详情）。 同样的原因，选择器`(&gaga).PrintName`和`(&gaga).SetAge`可以看作（也可以不看作）是`(&gaga.Person).PrintName`和`(&gaga.Person).SetAge`的缩写。

`Name`被称为类型`Singer`的一个提升字段（promoted field）。 `PrintName`被称为类型`Singer`的一个提升方法（promoted method）。

注意：我们也可以使用选择器`gaga.SetAge`，但是只有在`gaga`是一个可寻址的类型为`Singer`的值的情况下。 它只不过是`(&gaga).SetAge`的一个语法糖。

在上面的例子中，`c.B.A.FieldX`称为选择器表达式`c.FieldX`、`c.B.FieldX`和`c.A.FieldX`的完整形式。 类似的，`c.B.A.MethodA`可以称为`c.MethodA`、`c.B.MethodA`和`c.A.MethodA`的完整形式。

如果一个选择器的完整形式中的所有中部项均对应着一个内嵌字段，则中部项的数量称为此选择器的深度。 比如，上面的例子中的选择器`c.MethodA`的深度为2，因为此选择器的完整形式为`c.B.A.MethodA`，并且`B`和`A`都对应着一个内嵌字段。

### 选择器遮挡和碰撞

一个值`x`（这里我们总认为它是可寻址的）可能同时拥有多个最后一项相同的选择器，并且这些选择器的中间项均对应着一个内嵌字段。 对于这种情形（假设最后一项为`y`）：

- 只有深度最浅的一个完整形式的选择器（并且最浅者只有一个）可以被缩写为`x.y`。 换句话说，`x.y`表示深度最浅的一个选择器。其它完整形式的选择器被此最浅者所遮挡（压制）。
- 如果有多个完整形式的选择器同时拥有最浅深度，则任何完整形式的选择器都不能被缩写为`x.y`。 我们称这些同时拥有最浅深度的完整形式的选择器发生了碰撞。

如果一个方法选择器被另一个方法选择器所遮挡，并且它们对应的方法描述是一致的，那么我们可以说第一个方法被第二个覆盖（overridden）了。

举个例子，假设`A`、`B`和`C`为三个定义类型：

    type A struct {
    	x string
    }
    func (A) y(int) bool {
    	return false
    }

    type B struct {
    	y bool
    }
    func (B) x(string) {}

    type C struct {
    	B
    }

下面这段代码编译不通过，原因是选择器`v1.A.x`和`v1.B.x`的深度一样，所以它们发生了碰撞，结果导致它们都不能被缩写为`v1.x`。 同样的情况发生在选择器`v1.A.y`和`v1.B.y`身上。

    var v1 struct {
    	A
    	B
    }

    func f1() {
    	_ = v1.x // error: 模棱两可的v1.x
    	_ = v1.y // error: 模棱两可的v1.y
    }

下面的代码编译没问题。选择器`v2.C.B.x`被另一个选择器`v2.A.x`遮挡了，所以`v2.x`实际上是选择器`v2.A.x`的缩写形式。 因为同样的原因，`v2.y`是选择器`v2.A.y`（而不是选择器`v2.C.B.y`）的缩写形式。

    var v2 struct {
    	A
    	C
    }

    func f2() {
    	fmt.Printf("%T \n", v2.x) // string
    	fmt.Printf("%T \n", v2.y) // func(int) bool
    }

一个被遮挡或者碰撞的选择器并不妨碍更深层的选择器被提升，如下例所示中的`.M`和`.z`：

    package main

    type x string
    func (x) M() {}

    type y struct {
    	z byte
    }

    type A struct {
    	x
    }
    func (A) y(int) bool {
    	return false
    }

    type B struct {
    	y
    }
    func (B) x(string) {}

    func main() {
    	var v struct {
    		A
    		B
    	}
    	//_ = v.x // error: 模棱两可的v.x
    	//_ = v.y // error: 模棱两可的v.y
    	_ = v.M // ok. <=> v.A.x.M
    	_ = v.z // ok. <=> v.B.y.z
    }

一个不寻常的但需要注意的细节是：来自不同库包的两个非导出方法（或者字段）将总是被认为是两个不同的标识符，即使它们的名字完全一致。 因此，当它们的属主类型被同时内嵌在同一个结构体类型中的时候，它们绝对不会相互碰撞或者遮挡。 举个例子，下面这个含有两个库包的Go程序编译和运行都没问题。 但是，如果将其中所有出现的`m()`改为`M()`，则此程序将编译不过。 原因是`A.M`和`B.M`碰撞了，导致`c.M`为一个非法的选择器。

    package foo // import "x.y/foo"

    import "fmt"

    type A struct {
    	n int
    }

    func (a A) m() {
    	fmt.Println("A", a.n)
    }

    type I interface {
    	m()
    }

    func Bar(i I) {
    	i.m()
    }

    package main

    import "fmt"
    import "x.y/foo"

    type B struct {
    	n bool
    }

    func (b B) m() {
    	fmt.Println("B", b.n)
    }

    type C struct{
    	foo.A
    	B
    }

    func main() {
    	var c C
    	c.m()      // B false
    	foo.Bar(c) // A 0
    }

### 为内嵌了其它类型的结构体类型声明的隐式方法

上面已经提到过，类型`Singer`和`*Singer`都有一个`PrintName`方法，并且类型`*Singer`还有一个`SetAge`方法。 但是，我们从没有为这两个类型声明过这几个方法。这几个方法从哪来的呢？

事实上，假设结构体类型`S`内嵌了一个类型（或者类型别名）`T`，并且此内嵌是合法的，

- 对内嵌类型`T`的每一个方法，如果此方法对应的选择器既不和其它选择器碰撞也未被其它选择器遮挡，则编译器将会隐式地为结构体类型`S`声明一个同样描述的方法。 继而，编译器也将为指针类型`*S` 隐式声明一个相应的方法。
- 对类型`*T`的每一个方法，如果此方法对应的选择器既不和其它选择器碰撞也未被其它选择器遮挡，则编译器将会隐式地为类型`*S`声明一个同样描述的方法。

简单说来，

- 类型`struct{T}`和`*struct{T}`均将获取类型`T`的所有方法。
- 类型`*struct{T}`、`struct{*T}`和`*struct{*T}`都将获取类型`*T`的所有方法。

下面展示了编译器为类型`Singer`和`*Singer`隐式声明的三个（提升）方法：

    // 注意：这些声明不是合法的Go语法。这里这样表示只是为了
    // 解释目的。它们有助于解释提升方法值是如何被估值的。
    func (s Singer) PrintName = s.Person.PrintName
    func (s *Singer) PrintName = s.Person.PrintName
    func (s *Singer) SetAge = s.Person.SetAge

右边的部分为各个提升方法相应的完整形式选择器形式。

从方法一文中，我们得知我们不能为无名的结构体类型（和基类型为无名结构体类型的指针类型）声明方法。 但是，通过类型内嵌，这样的类型也可以拥有方法。

如果一个结构体类型内嵌了一个实现了一个接口类型的类型（此内嵌类型可以是此接口类型自己），则一般说来，此结构体类型也实现了此接口类型，除非发生了选择器碰撞和遮挡。 比如，上例中的结构体类型和以它为基类型的指针类型均实现了接口类型`I`。

请注意：一个类型将只会获取它（直接或者间接）内嵌了的类型的方法。 换句话说，一个类型的方法集由为类型直接（显式或者隐式）声明的方法和此类型的底层类型的方法集组成。 比如，在下面的例子中，

- 类型`Age`没有方法，因为代码中既没有为它声明任何方法，它也没有内嵌任何类型，。
- 类型`X`有两个方法：`IsOdd`和`Double`。 其中`IsOdd`方法是通过内嵌类型`MyInt`而得来的。
- 类型`Y`没有方法，因为它所内嵌的类型`Age`没有方法，另外代码中也没有为它声明任何方法。
- 类型`Z`只有一个方法：`IsOdd`。 此方法是通过内嵌类型`MyInt`而得来的。 它没有获取到类型`X`的`Double`方法，因为它并没有内嵌类型`X`。

    type MyInt int
    func (mi MyInt) IsOdd() bool {
    	return mi%2 == 1
    }

    type Age MyInt

    type X struct {
    	MyInt
    }
    func (x X) Double() MyInt {
    	return x.MyInt + x.MyInt
    }

    type Y struct {
    	Age
    }

    type Z X

### 提升方法值的正规化和估值

假设`v.m`是一个合法的提升方法表达式，在编译时刻，编译器将把此提升方法表达式正规化。 正规化过程分为两步：首先找出此提升方法表达式的完整形式；然后将此完整形式中的隐式取地址和解引用操作均转换为显式操作。

和其它方法值估值的规则一样，对于一个已经正规化的方法值表达式`v.m`，在运行时刻，当`v.m`被估值的时候，属主实参`v`的估值结果的一个副本将被存储下来以供后面调用此方法值的时候使用。

以下面的代码为例：

- 提升方法表达式`s.M1`的完整形式为`s.T.X.M1`。 将此完整形式中的隐式取地址和解引用操作转换为显式操作之后的结果为`(*s.T).X.M1`。 在运行时刻，属主实参`(*s.T).X`被估值并且估值结果的一个副本被存储下来以供后用。 此估值结果为`1`，这就是为什么调用`f()`总是打印出`1`。
- 提升方法表达式`s.M2`的完整形式为`s.T.X.M2`。 将此完整形式中的隐式取地址和解引用操作转换为显式操作之后的结果为`(&(*s.T).X).M2`。 在运行时刻，属主实参`&(*s.T).X`被估值并且估值结果的一个副本被存储下来以供后用。 此估值结果为提升字段`s.X`（也就是`(*s.T).X`）的地址。 任何对`s.X`的修改都可以通过解引用此地址而反映出来，但是对`s.T`的修改是不会通过此地址反映出来的。 这就是为什么两个`g()`调用都打印出了`2`。

    package main

    import "fmt"

    type X int

    func (x X) M1() {
    	fmt.Println(x)
    }

    func (x *X) M2() {
    	fmt.Println(*x)
    }

    type T struct { X }

    type S struct { *T }

    func main() {
    	var t = &T{X: 1}
    	var s = S{T: t}
    	var f = s.M1 // <=> (*s.T).X.M1
    	var g = s.M2 // <=> (&(*s.T).X).M2
    	s.X = 2
    	f() // 1
    	g() // 2
    	s.T = &T{X: 3}
    	f() // 1
    	g() // 2
    }

### 接口类型内嵌各种类型

接口类型可以内嵌各种类型。详情请阅读接口一文。

### 一个有趣的类型内嵌的例子

在本文的最后，让我们来看一个有趣的例子。 此例子程序将陷入死循环并会因堆栈溢出而崩溃退出。 如果你已经理解了多态和类型内嵌，那么就不难理解为什么此程序将死循环。

    package main

    type I interface {
    	m()
    }

    type T struct {
    	I
    }

    func main() {
    	var t T
    	var i = &t
    	t.I = i
    	i.m() // 将调用t.m()，然后再次调用i.m()，......
    }