我们知道,在Go中定义了结构体后,可以给该结构体定义方法。如下:
type customer struct {
balance string
}
func (c customer) SetBalance(v float64) {
c.balance = v
}
func (c *customer) UpdateBalance(v float64) {
c.balance = v
}那么接收者的值类型和指针类型他们之间有什么区别?我们在定义方法时,接收者是该选择使用值类型还是选择使用指针类型呢?
01 方法接收者是值类型
在Go中,大家都听过的一切都是拷贝。所以,当方法的接收者是一个值类型时,实际上是对原来对象的一个拷贝,然后让该对象的拷贝再来调用对应的方法。在方法中对接收者的任何改变,都不会影响原对象。
下面通过一段具体的示例来说明。
type customer struct {
balance float64
}
func (c customer) add(v float64) {
c.balance += v
}
func main() {
c := customer{balance: 100.}
c.add(50.)
fmt.Printf("balance: %.2f\n", c.balance)
}因为在add方法中,接收者是值类型,在执行c.add(50.)函数时,实际上是对c进行了拷贝,然后改变了新拷贝的对象的balance。所以,最终c.balance的结果没有任何改变,依然是100。如图所示:

02 方法接收者是指针类型
如果接收者的类型是指针,那么,我们传递给方法的是原对象的地址,依然是值拷贝,这里的值是地址值,而非是原对象的拷贝。这时,在方法中对接收者的任何改变,都会作用到原对象上。
依然是上面的示例,我们将接收者类型更改成指针。
type customer struct {
balance float64
}
func (c *customer) add(v float64) {
c.balance += v
}
func main() {
c := customer{balance: 100.}
c.add(50.)
fmt.Printf("balance: %.2f\n", c.balance)
}因为接收者是指针类型,所以,对balance的更改实际上是对原对象的更改,最终结果会输出150。如图所示:

03 接收者的类型该如何选择
在定义结构体方法时,接收者类型是使用值类型还是指针类型呢?下面我们列出一些常见的选择依据来帮助我们选择使用哪种类型。
接收者必须是指针类型的场景:
type slice []int
func (s *slice) add(element int) {
*s = append(*s, element)
}接收者建议使用指针类型的场景:
接收者必须是值类型的场景:
接收者建议使用值类型的场景:
04 一个示例
下面我们看一个稍微复杂点示例。在该示例中,customer结构体中包含了一个指针类型的字段。示例如下:
type customer struct {
card *card
}
type card struct {
balance float64
}
func (c customer) add(operation float64) {
c.card.balance += operation
}
func main() {
c := customer{card: &card{
balance: 100,
}}
c.add(50.)
fmt.Printf("balance: %.2f\n", c.card.balance)
}在该示例中,balance是card结构体中的字段,而customer中通过指针引入了card。同时,方法的接收者类型我们依然使用的是值类型,但最终结果依然会改变原对象中balance的值。