在结构体中如果一个字段声明只有类型而没有指定名称,则这个字段叫做匿名字段。匿名字段的类型必须由一个数据类型的名称(比如int、string、Sortable等)或者一个非接口类型对应的指针类型的名称(比如*string、*p.T等)代表。更重要的是,代表匿名字段类型的非限定名称将被隐含地作为该字段的名称,如:
type Sequence struct{
len int
cap int
Sortable
sortableArray sort.Interface
}
其中字段Sortable只有类型没有指定名称,它就是匿名字段,go编译器缺省把Sortable作为它的名字,即上述定义等价于:
type Sequence struct{
len int
cap int
Sortable Sortable
sortableArray sor.Interface
}
如果匿名字段类型是一个指针类型的话,那么这个指针类型所指的数据类型的非限定名称会作为该字段的名称,即:
type Anonymities struct{
T1
*T2
P.T3
*P.T4
}
该结构体类型包含了4个匿名字段,其中T1和P.T3为非指针的数据类型,它们隐含的名称分别为T1和T3; 而*T2和*P.T4为指针类型,它们隐含的名称分别为T2和T4。
这里也变相地解释了什么是非限定表示符?
非限定标识符指的是不包含代码包名称和点“.”的标识符。
另外,即便是匿名的名称也不能与结构体内的其他字段名称重复。
结构体类型中的嵌入字段比接口类型间的嵌入有着更加复杂的含义。嵌入类型所附带的方法都会无条件地与被嵌入的结构体类型关联在一起,即它们也成为了被嵌入的结构体类型的方法。
这意味着,结构体类型自动地实现了它包含的所有嵌入类型所实现的接口类型。
// 定义一个接口
type Sortable interface{
sort.Interface
Sort()
}
// 定义一个结构体
type Sequence struct{
Sortable
sorted bool
}
其中存储和操作可排序序列的功能都交给了匿名字段Sortable,然后又定义了一个bool类型的字段sorted,并用它表示排序序列是否已经被排序。
此时如果我们有一个Sequence类型的值seq,那么就可以直接在这个值上调用Sortable接口类型中包含的那些方法了,如seq.Sort(),在这种情况下,嵌入类型Sortable的Sort()方法被隐藏了。
被隐藏带来一些便利——可以方便地声明一个同名方法,同时不丢弃原有功能:
func (s *Sequence) Sort() {
s.Sortable.Sort() // 不丢弃原有能力
s.sorted = true // 添加新能力
}
注意,如果这两个Sort()方法的名称相同但签名不同,那么嵌入类型Sortable的方法Sort()就被隐藏了,这时在Sequence的类型值上调用Sort()方法的时候,就必须依据被该类型的Sort()方法的签名来编写调用表达式。