Golang初级系列教程-结构体匿名字段-Anonymous fields in structs
Go
结构体中支持匿名字段。下面的几个例子中,会展示如何构造以及这种用法的好处。
下面的代码中,定义了结构体 Kitchen
,包含字段 numOfPlates
。另一个结构体 House
,包含 Kitchen
的一个实例,但是没有变量名——即匿名。
package main
import "fmt"
type Kitchen struct {
numOfPlates int
}
type House struct {
Kitchen //anonymous field
numOfRooms int
}
func main() {
h := House{Kitchen{10}, 3} //to initialize you have to use composed type name.
fmt.Println("House h has this many rooms:", h.numOfRooms) //numOfRooms is a field of House
fmt.Println("House h has this many plates:", h.numOfPlates) //numOfPlates is a field of anonymous field Kitchen, so it can be referred to like a field of House
fmt.Println("The Kitchen contents of this house are:", h.Kitchen) //we can refer to the embedded struct in its entirety by referring to the name of the struct type
}
House h has this many rooms: 3
House h has this many plates: 10
The Kitchen contents of this house are: {10}
首先需要强调的一点是,由于 Kitchen
是 House
的匿名字段,House
可以访问 Kitchen
的属性,就如同这些属性是 House
本身的一样。为了说明这一点,让我们同 Java
代码做一个对比。
public class Kitchen {
public int numOfPlates;
}
public class House {
public Kitchen kitchen;
}
//and in main
public static void main(String[] args) {
House h = new House();
h.kitchen.numOfPlates = 10; //referred as a sub field item.
}
其次,匿名字段通过其类型名也可以访问。所以,对于上面的的例子,可以通过 h.kitchen
访问,这时打印盘子个数应该写为 fmt.Println(h.Kitchen.numOfPlates)
。
第三点,定义时,必须使用类型名,如同上面所示 h := House{Kitchen{10}, 3}
。如果不写上类型和其对应的 {}
,如同下面的两种定义方式都会产生编译错误,h := House{{10}, 3}
,h := House{10, 3}
。
匿名类型命名冲突
当匿名字段包含相同的字段命名或者外围结构体含有和匿名结构相同的字段名时,程序会如何运行?当外围结构体含有和匿名结构相同的字段时,默认访问外围结构体的字段。下面的代码中,kitchen
和 House
都有个字段名为 numOfLamps
,由于 House
包含 kitchen
,所以 House
的 numOfLamps
会覆盖 kitchen
的。如果想访问 kitchen
的 numOfLamps
,则需要通过使用类型名 h.Kitchen.numOfLamps
。
package main
import "fmt"
type Kitchen struct {
numOfLamps int
}
type House struct {
Kitchen
numOfLamps int
}
func main() {
h := House{Kitchen{2}, 10} //kitchen has 2 lamps, and the House has a total of 10 lamps
fmt.Println("House h has this many lamps:", h.numOfLamps) //this is ok - the outer House's numOfLamps hides the other one. Output is 10.
fmt.Println("The Kitchen in house h has this many lamps:", h.Kitchen.numOfLamps) //we can still reach the number of lamps in the kitchen by using the type name h.Kitchen
}
House h has this many lamps: 10
The Kitchen in house h has this many lamps: 2
所以,当结构体不同级组合遇到命名冲突时,存在字段的解析规则。但是,对于同一级的结构体字段命名冲突,程序无法完成解析——需要自行解决。
下面代码中,Kitchen
和 Bedroom
都包含 numOfLamps
字段,并且都是 House
的匿名字段。当访问 House.numOfLamps
时,Go
编译器不知道是访问哪个,进而抛出错误。
package main
import "fmt"
type Kitchen struct {
numOfLamps int
}
type Bedroom struct {
numOfLamps int
}
type House struct {
Kitchen
Bedroom
}
func main() {
h := House{Kitchen{2}, Bedroom{3}} //kitchen has 2 lamps, Bedroom has 3 lamps
fmt.Println("Ambiguous number of lamps:", h.numOfLamps) //this is an error due to ambiguousness - is it Kitchen.numOfLamps or Bedroom.numOfLamps
}
8g -o _go_.8 structs2.go
structs2.go:20: ambiguous DOT reference House.numOfLamps
make: *** [_go_.8] Error 1
只能通过使用类型名的方式进行访问,如下面代码所示。
package main
import "fmt"
type Kitchen struct {
numOfLamps int
}
type Bedroom struct {
numOfLamps int
}
type House struct {
Kitchen
Bedroom
}
func main() {
h := House{Kitchen{2}, Bedroom{3}}
fmt.Println("House h has this many lamps:", h.Kitchen.numOfLamps + h.Bedroom.numOfLamps) //refer to fields via type name
}
House h has this many lamps: 5
Golang一种神奇的语言,让我们一起进步