方法集
Golang中每一个类型所对应的方法,我们把它称为方法集,按方法接收者来划分可以分为:实体类型和指针类型。如下例子:
1 | type Student struct { |
- 实体类型Student接收者方法集包括:showName()
- 指针类型Student接收者方法集包括:showName(),setName()
Go中规定了如下规则
- 类型T的方法集包含receiver T的方法
- 类型*T的方法集包含receiver T和receiver *T的方法
但需要注意的是实体类型也可以调用指针类型接收者的方法的,如下例子:
1 | s := Student{} |
这是因为编译器会进行自动类型转换,所以无论是实体类型还是指针类型都可以随意调用自身的任何方法。因此我们类型调用自身方法时无需在意方法集的概念,方法集需要和接口一起来使用。
方法集和接口
接口代表一个或者多个方法签名的集合,因此只要任何类型具有该接口所有方法签名的话,就可以说它实现了该接口,无需类似Java/C++使用显示implement等关键字去声明。
一个非空方法接口(iface)是由接口表和数据指针所构成的。那么使用实体类型或者指针类型去实现接口有什么区别吗?
- 如果是实体类型去实现接口所定义的方法集,那么T和*T都可以说实现了该接口
- 如果是指针类型去实现接口所定义的方法集,那么只有*T才实现了该接口
如下例子:
1 | type IPeople interface { |
可以看到编译时报People实体类型没有实现IPeople接口,可以修改为var i IPeople = &people
或者将setName方法接收者修改为实体类型。
方法集和组合
总所周知,Go中并没有类似Java/C++等传统OOP的extends关键字,这是因为Go提倡使用组合来实现继承的行为,如下面的例子。
1 | type People struct { |
那么实体类型组合和指针类型组合又有什么区别呢?
1 | type Student1 struct { |
首先是默认值不同
struct
是复合类型,因此默认值是所包含类型的零值- 指针是引用类型,零值为nil
再其次就是方法集的不同
- 类型S包含匿名T类型,则S类型和*S类型都包含T的方法集
- 类型S包含匿名* T类型, 则S类型和*S类型都包含T和 *T的方法集
- 无论类型S包含匿名*T类型还是T类型, *S都包含T和 *T的方法集