struct 是 Go 中常用的数据类型,可以用来表示任何想要的数据,在一些组合的场景中,我们会把一个类型、一个 struct 嵌入到其他的 struct 里面,这里就是所说的类型嵌套,但是并不是所有的类型都可以嵌套,这里有两个基本规则
- 一个类型 T 可以被内嵌,除非它表示一个命名的指针类型,或者基类型是指针、接口的指针类型
- 指针类型 *T (T 是一个基类型为指针的类型)可以内嵌,除非它表示一个指针或者接口类型
func main() {
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
type Peng struct {
// 以下类型可以被内嵌
Encoder
Person
//*Person
Alias
//*Alias
AliasPtr
int
//*int
}
type Hong struct {
// 以下类型不可以被内嵌
AliasPP // 基类型是指针
*Encoder // 基类型是接口
*AliasPtr // 基类型是指针
IntPtr // 命名指针类型
*IntPtr // 基类型是指针
*chan int // 未命名类型
struct{ age int } // 未命名类型
map[string]int // 未命名类型
[]int64 // 未命名类型
func()
}
}
此外,一个类型也不能和基类型同时出现,比如 int 和 *int 就不能同时内嵌,同理,一个 struct 也不能内嵌它本身
类型内嵌的最大意义其实是扩展了方法的使用范围,当类型内嵌以后,嵌入类型就拥有了被嵌入类型的方法,在其他面向对象的语言中,大都是通过继承来实现类似的功能,但 Go 是基于组合来做的
package main
type A struct {
FieldX 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 is a promoted field of C
_ = c.FieldX // FieldX is a promoted field
c.B.A.MethodA()
c.B.MethodA()
c.A.MethodA()
c.MethodA() // MethodA is a promoted method of C
}
在类型内嵌里面,有一个类型提升的概念,就是内嵌类型拥有了被内嵌类型的所有属性,并且可以直接引用,但是类型提升会带来另一个类型碰撞的问题,假设 struct A 拥有 B C两个类型,然后 B 拥有一个 X变量,C 拥有一个 X 方法,这时候如果使用 A.X 就会编译不过,因为你根本就不知道这个 X 指代什么