在 Go 中,我们可以通过为一个类型定义方法来为其添加行为。方法的接收者(receiver)可以是值类型或指针类型。
值接收者和指针接收者的主要区别在于它们对待方法中的接收者参数时是否会进行值拷贝。具体来说,当使用值接收者时,调用方法时会将接收者参数的值复制一份,然后将这个副本传入方法中进行操作。而当使用指针接收者时,调用方法时会将接收者参数的地址传入方法中进行操作,即直接操作原始数据。
使用值接收者的情况:
- 当类型的实例的内容不需要被修改时
- 当类型的实例作为参数进行传递时不需要被修改
- 当类型的实例是小型值类型时
使用指针接收者的情况:
- 当类型的实例的内容需要被修改时
- 当类型的实例作为参数进行传递时需要被修改
- 当类型的实例是大型引用类型时
需要注意的是,如果一个类型的所有方法都是值接收者或者都是指针接收者,则该类型仍然可以在两种方式下正常工作。通常,建议按照以下规则选择接收者类型:
- 如果类型的方法只有少量需要修改其接收者时,可以考虑使用值接收者。
- 如果类型的方法大部分需要修改其接收者时,或者类型本身是大型引用类型时,建议使用指针接收者。
- 如果无法确定应该使用哪种类型的接收者,则可以考虑使用值接收者。
package main
import "fmt"
type MyInt int
// 值接收器
func (m MyInt) Add(num int) {
m += MyInt(num)
fmt.Printf("value receiver: %v\n", m)
}
// 指针接收器
func (m *MyInt) Increment() {
*m++
fmt.Printf("pointer receiver: %v\n", *m)
}
func main() {
i := MyInt(10)
// 值接收器方法调用
i.Add(5)
fmt.Println(i)
// 指针接收器方法调用
(&i).Increment()
fmt.Println(i)
i.Increment()
fmt.Println(i)
}
在这个示例中,我们定义了一个自定义类型 MyInt,并为其定义了一个值接收器方法 Add 和一个指针接收器方法 Increment。在 main 函数中,我们创建了一个 MyInt 类型的变量 i,并先后调用了这两个方法。当我们调用值接收器方法 Add 时,i 的值不会改变,因为该方法只是操作了 i 的副本。而当我们调用指针接收器方法 Increment 时,i 的值会被直接修改。
需要注意的是,虽然我们在第三个方法调用中没有使用指针语法,但 Go 编译器会自动将其转换为指针形式,因此该调用仍然属于指针接收器方法调用。