持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第20天,点击查看活动详情
方法集
Golang中每一个类型所对应的方法,我们把它称为方法集,按方法接收者来划分可以分为:实体类型和指针类型。如下例子:
type Student struct {
Age int
Name string
}
// 实体类型方法接收者
func (s Student) showName() {
fmt.Println("name="+s.Name)
}
// 指针类型方法接收者
func (s *Student) setName(name string) {
s.Name = name
}
- 实体类型Student接收者方法集包括:showName()
- 指针类型Student接收者方法集包括:showName(),setName()
Go中规定了如下规则
- 类型T的方法集包含receiver T的方法
- 类型*T的方法集包含receiver T和receiver *T的方法
但需要注意的是实体类型也可以调用指针类型接收者的方法的,如下例子:
s := Student{}
s.setName("Tom") // (&s).setName("Tom")
这是因为编译器会进行自动类型转换,所以无论是实体类型还是指针类型都可以随意调用自身的任何方法。因此我们类型调用自身方法时无需在意方法集的概念,方法集需要和接口一起来使用。
方法集和接口
接口代表一个或者多个方法签名的集合,因此只要任何类型具有该接口所有方法签名的话,就可以说它实现了该接口,无需类似Java/C++使用显示implement等关键字去声明。
一个非空方法接口(iface)是由接口表和数据指针所构成的。那么使用实体类型或者指针类型去实现接口有什么区别吗?
- 如果是实体类型去实现接口所定义的方法集,那么T和*T都可以说实现了该接口
- 如果是指针类型去实现接口所定义的方法集,那么只有*T才实现了该接口
如下例子:
type IPeople interface {
setName(name string)
}
type People struct {
Age int
Name string
}
func (p People) showName() {
fmt.Println("name=" + p.Name)
}
func (p *People) setName(name string) {
p.Name = name
}
func main() {
people := People{}
var i IPeople = people // 编译失败:People does not implement IPeople (setName method has pointer receiver)
}
可以看到编译时报People实体类型没有实现IPeople接口,可以修改为var i IPeople = &people或者将setName方法接收者修改为实体类型。
方法集和组合
总所周知,Go中并没有类似Java/C++等传统OOP的extends关键字,这是因为Go提倡使用组合来实现继承的行为,如下面的例子。
type People struct {
Age int
Name string
}
type Student struct {
People
}
那么实体类型组合和指针类型组合又有什么区别呢?
type Student1 struct {
People
}
type Student2 struct {
*People
}
首先是默认值不同
struct是复合类型,因此默认值是所包含类型的零值- 指针是引用类型,零值为nil
再其次就是方法集的不同
- 类型S包含匿名T类型,则S类型和*S类型都包含T的方法集
- 类型S包含匿名* T类型, 则S类型和*S类型都包含T和 *T的方法集
- 无论类型S包含匿名*T类型还是T类型, *S都包含T和 *T的方法集