概念
函数和方法
在 Go 语言中,我们可以为自定义类型定义方法。方法是一个与特定类型关联的函数。方法可以被定义在值类型上或指针类型上。这两种方法分别称为值方法和指针方法。
// 函数
func Hello() {
fmt.Println("Hello World!")
}
type Welcome struct{}
// 方法
func (w Welcome) Hello() {
fmt.Println("Hello World!")
}
接收者
- 值方法: 是定义在值类型上的方法,它们使用值作为接收器(即方法调用的目标对象)。
- 指针方法: 是定义在指针类型上的方法,它们使用指针作为接收器。
// 指针方法
func (w *Welcome) SetName(name string) {
w.name = name
}
// 值方法
func (w Welcome) Welcome() {
fmt.Printf("Welcome %s", w.name)
}
区别
- 如果你在类型上定义了一个值方法,那么它不会修改接收者的原始值,如果定义了指针方法,则可以修改接收者的原始值
- 值方法可以通过指针和值类型的变量调用
- 指针方法只能通过指针变量来调用,但是如果某个值是可寻址的(addressable,或者说左值),那么编译器会在值调用指针方法时自动插入取地址符,使得在此情形下看起来像指针方法也可以通过值来调用
func TestWelcome(t *testing.T) {
// 正确: 值类型调用值类型的方法
NewWelcome().Hello()
// 正确: 指针类型的变量可以调用指针方法和值方法
NewWelcomePtr().SetName("Harry")
NewWelcomePtr().Welcome()
// 正常: w 为值类型的变量,但是因为 w 是可寻址的,所以编译器会自动转换
w := NewWelcome()
w.SetName("Harry")
w.Welcome()
// 报错: cannot call pointer method SetName on Welcome
// 函数返回值是不可寻址的
NewWelcome().SetName("Harry")
}
如何选择
- 如果需要修改接收者的原始值,则必须使用指针方法
- 当不希望方法修改接收者的原始值时使用值方法
- 当不需要修改原始值且对象比较大时希望避免因值传递带来的性能开销时,使用指针方法。
- 当不需要修改原始值且对象比较简单时使用值方法
扩展
自动转换
Go 语言会自动处理值和指针之间的转换。即使定义了指针方法,也可以通过值调用它(Go 会自动转换为指针);同样,定义了值方法,也可以通过指针调用它(Go 会自动解引用)。
左值和右值
值类型 | 区别 |
---|---|
左值 | 可寻址,可通过 & 取地址符获取内存地址 |
右值 | 不可寻址,没有分配内存地址 |
总结
- 值方法内的修改不会影响接收者原始值,指针方法可以
- 指针类型可以调用指针方法和值方法
- 值类型只能调用值方法,但是如果该值是可寻址的情况编译器会自动转换,使得看起来像值类型调用了指针方法