30. Go值接收者和指针接收者的区别
Go中的⽅法能给⽤户⾃定义的类型添加新的⾏为。它和函数的区别在于⽅法有⼀个接收者,给⼀个函数添加⼀个接收者,那么它就变成了⽅法。接收者可以是值接收者,也可以是指针接收者。
在调⽤⽅法的时候,值类型既可以调⽤值接收者的⽅法,也可以调⽤指针接收者的⽅法;指针类型既可以调⽤指针接收者的⽅法,也可以调⽤值接收者的⽅法。
也就是说,不管⽅法的接收者是什么类型,该类型的值和指针都可以调⽤,不必严格符合接收者的类型。
代码示例 :
import "fmt"
type Person struct {
age int
}
// 值参数
func (p Person) getAge() int {
return p.age
}
// 指针参数
func (p *Person) incrAge() {
p.age += 1
}
func main() {
// 初始化p1为值类型
p1 := Person{age: 18}
// 值类型 调用方法参数也是值类型
fmt.Println(p1.getAge())
// 值类型 调用方法是指针类型
p1.incrAge()
fmt.Println(p1.getAge())
// 初始化p2为指针类型
p2 := &Person{age: 100}
fmt.Println(p2.getAge())
p2.incrAge()
fmt.Println(p2.getAge())
}
输出结果为
因⽽呢,我们是使⽤值接收者还是指针接收者,不是由该⽅法是否修改了调⽤者(也就是接收者)来决定,⽽是应该基于该类型的本质。
如果类型具备“原始的本质”,也就是说它的成员都是由 Go 语⾔⾥内置的原始类型,如字符串,整型值等,那就定义值接收者类型的⽅法。像内置的引⽤类型,如 slice,map,interface,channel,这些类型⽐较特殊,声明他们的时候,实际上是创建了⼀个 header, 对于他们也是直接定义值接收者类型的⽅法。这样,调⽤函数时,是直接 copy 了这些类型的 header,⽽ header 本⾝就是为复制设计的。
如果类型具备⾮原始的本质,不能被安全地复制,这种类型总是应该被共享,那就定义指针接收者的⽅法。⽐如 go 源码⾥的⽂件结构体(struct File)就不应该被复制,应该只有⼀份实体。