Go语言有一个特性让我们只声明一个成员对应的数据类型而不指名成员的名字,这类成员就叫匿名成员。匿名成员的数据类型必须是命名的类型或指向一个命名的类型的指针。
匿名成员
type Shape struct {
}
type Circle struct {
Shape //Shape匿名成员
}
任何类型都可以作为结构体的匿名成员,使用匿名成员不仅可以用简短的点运算符语法选择匿名成员嵌套的成员,也可以用于访问匿名类型的方法集。实际上,外层的结构体不仅仅是获得了匿名成员类型的所有成员,而且也或得了该类型导出的全部的方法。这个机制可以用于将一个有简单行为的对象组合成有复杂行为的对象。组合是Go语言中面向对象编程的核心。
匿名成员及其方法
type Shape struct {
Data int
}
func (p *Shape) GetShapeArea() int {
fmt.Println("---------GetShapeArea----------")
fmt.Println("GetShapeArea",p.Data,p)
p.Data = 20 //p是指针传递,实参和形参指针指向同一个对象,所以这里修改会直接影响到实参对象
return 0
}
func (s Shape) ShowShapeImage() {
fmt.Println("---------ShowShapeImage----------")
fmt.Println("ShowShapeImage",s.Data,s)
s.Data = 30 //s只是值传递的副本,这里修改形参并不会影响实参
}
type Circle struct {
//如果嵌入类型为指针类型,调用其方法时必须初始化,
//不然调用Shape的方法会直接报错(panic: runtime error: invalid memory address or nil pointer dereference),因为*Shape为nil,获取不到Shape对象,
//调用*Shape的方法时,如果不使用接收者*Shape指针的对象,就不会报错,否则会报错(panic: runtime error: invalid memory address or nil pointer dereference)
//*Shape
//如果嵌入类型为非指针类型,调用其方法时可以不用初始化,因为struct属于值类型,默认为零值
Shape
}
func (p *Circle) GetCircleRadius() int {
fmt.Println("---------GetCircleRadius----------")
p.GetShapeArea()
p.ShowShapeImage()
return 0
}
func (c Circle)GetCircleColor() int {
fmt.Println("---------GetCircleColor----------")
c.ShowShapeImage()
c.GetShapeArea()
return 0
}
调用
1.嵌入类型为Shape
1).结构体类型对象为Circle
c := Circle{}
c.ShowShapeImage()
c.GetShapeArea()
c.GetCircleRadius()
c.GetCircleColor()
fmt.Println(c.Data)
运行结果
---------ShowShapeImage----------
ShowShapeImage 0 {0}
---------GetShapeArea----------
GetShapeArea 0 &{0}
---------GetCircleRadius----------
---------GetShapeArea----------
GetShapeArea 20 &{20}
---------ShowShapeImage----------
ShowShapeImage 20 {20}
---------GetCircleColor----------
---------ShowShapeImage----------
ShowShapeImage 20 {20}
---------GetShapeArea----------
GetShapeArea 20 &{20}
20
2).结构体类型对象为*Circle
c := &Circle{}
c.ShowShapeImage()
c.GetShapeArea()
c.GetCircleRadius()
c.GetCircleColor()
fmt.Println(c.Data)
运行结果
---------ShowShapeImage----------
ShowShapeImage 0 {0}
---------GetShapeArea----------
GetShapeArea 0 &{0}
---------GetCircleRadius----------
---------GetShapeArea----------
GetShapeArea 20 &{20}
---------ShowShapeImage----------
ShowShapeImage 20 {20}
---------GetCircleColor----------
---------ShowShapeImage----------
ShowShapeImage 20 {20}
---------GetShapeArea----------
GetShapeArea 20 &{20}
20
2.嵌入类型为*Shape
1).结构体类型对象为Circle
c := Circle{&Shape{10}}
c.ShowShapeImage()
c.GetShapeArea()
c.GetCircleRadius()
c.GetCircleColor()
fmt.Println(c.Data)
运行结果
---------ShowShapeImage----------
ShowShapeImage 10 {10}
---------GetShapeArea----------
GetShapeArea 10 &{10}
---------GetCircleRadius----------
---------GetShapeArea----------
GetShapeArea 20 &{20}
---------ShowShapeImage----------
ShowShapeImage 20 {20}
---------GetCircleColor----------
---------ShowShapeImage----------
ShowShapeImage 20 {20}
---------GetShapeArea----------
GetShapeArea 20 &{20}
20
2).结构体类型对象为*Circle
c := &Circle{&Shape{10}}
c.ShowShapeImage()
c.GetShapeArea()
c.GetCircleRadius()
c.GetCircleColor()
fmt.Println(c.Data)
运行结果
---------ShowShapeImage----------
ShowShapeImage 10 {10}
---------GetShapeArea----------
GetShapeArea 10 &{10}
---------GetCircleRadius----------
---------GetShapeArea----------
GetShapeArea 20 &{20}
---------ShowShapeImage----------
ShowShapeImage 20 {20}
---------GetCircleColor----------
---------ShowShapeImage----------
ShowShapeImage 20 {20}
---------GetShapeArea----------
GetShapeArea 20 &{20}
20
总结
不管 Circle 中包含了一个嵌入类型 *Shape或是Shape,那么 Circle 和 *Circle 的方法集合中都会包含接收者类型为 Shape 和 *Shape 的所有方法,只是嵌入类型如果是*Shape的话,在使用时需要将*Shape初始化(也就是赋值),不然调用在调用Shape的方法时会直接报错,调用*Shape的方法时,如果使用到接收者*Shape对象也会报错。
所以个人感觉结构体的数据量过大时,在使用嵌入类型时应尽量使用非指针类型,而嵌入类型的方法集应使用基于指针对象的方法
方法覆盖
如果我们给*Circle/Circle再添加一个与*Shape/Shape方法名相同的方法,那么*Shape/Shape的方法就会被*Circle/Circle的方法覆盖,因为Go语言没有方法重载,所以编译器认为只要是方法名相同就会被覆盖,而不是方法的签名,这里不仅仅只有方法才能覆盖方法,方法也可以覆盖成员变量,只要名字相同就可以覆盖,成员变量也可以覆盖方法。
func (c *Circle) GetShapeArea() {
fmt.Println("-------*Circle--GetShapeArea----------")
}
c := &Circle{&Shape{10}}
c.GetShapeArea()
运行结果:
-------*Circle--GetShapeArea----------