golang指针接收者和值接收者方法调用笔记
初学go时很多同学会把
值接收者
和指针接收者
的方法相互调用搞混淆,好多同学都只记得指针类型
可以调用值接收者方法
和指针接收者方法
,而值类型
只能调用值接收者方法
,其实不然,在某些情况下,值类型
也是可以调用指针接收者方法
的。最近又看到有同学发出了这样的疑问,所以打算记录一下,用以备忘、分享。
类型不同可以调用
package main
import (
"fmt"
)
type field struct {
name string
}
func (p *field) pointerMethod() {
fmt.Println(p.name)
}
func (p field) valueMethod() {
fmt.Println(p.name)
}
func main() {
fp := &field{name: "pointer"}
fv := field{name: "value"}
fp.pointerMethod()
fp.valueMethod()
fv.pointerMethod()
fv.valueMethod()
}
//output: pointer pointer value value
首先我们给field
定义了两个方法,一个是指针接收者的pointerMethod
,一个是值接收者valueMethod
。
然后我们创建了变量,fp
是指针类型,fv
是值类型。
fp
、fv
分别调用pointerMethod
、valueMethod
,可以看到他们都可以通过编译正常输出。
当类型和方法的接收者类型不同时,编译器会做一些操作:
在值类型
调用指针接收者方法
时,实际为(&fv).pointerMethod()
在指针类型
调用值接收者方法
时,实际为(*fp).valueMethod()
这里是类型不同可以相互调用的情况,再说下不能调用的情况。
类型不同不可以调用
不能调用的情况有两种:
值类型
不能被寻址- 用
指针接收者
实现接口
两种情况都是值类型
不能调用指针接收者方法
值类型
不能被寻址
如果值类型实体不能被寻址,那么它就不能调用指针接收者方法
package main
import (
"fmt"
)
type field struct {
name string
}
func (p *field) pointerMethod() {
fmt.Println(p.name)
}
func (p field) valueMethod() {
fmt.Println(p.name)
}
func NewFiled() field {
return field{name: "right value struct"}
}
func main() {
NewFiled().valueMethod()
NewFiled().pointerMethod()
}
运行代码报错:
./x.go:37:12: cannot call pointer method on NewFiled()
./x.go:37:12: cannot take the address of NewFiled()
- 看下青藤木鸟的解释:
看来编译器首先试着给
NewFoo()
返回的右值调用 pointer method,出错;然后试图给其插入取地址符,未果,就只能报错了。至于左值和右值的区别,大家感兴趣可以自行搜索一下。大致来说,最重要区别就是是否可以被寻址,可以被寻址的是左值,既可以出现在赋值号左边也可以出现在右边;不可以被寻址的即为右值,比如函数返回值、字面值、常量值等等,只能出现在赋值号右边。
用指针接收者
实现接口
使用指针接收者实现接口方法,那么只有指针类型的实体实现了接口
package main
import "fmt"
type human interface {
speak()
sing()
}
type man struct {
}
func (m man) speak() {
fmt.Println("speaking")
}
func (m *man) sing() {
fmt.Println("singing")
}
func main() {
var h human = &man{}
h.speak()
h.sing()
}
上面代码可以正常编译输出,但是我们把&man{}
修改为man{}
就编译不过了
func main() {
var h human = man{}
h.speak()
h.sing()
}
报错如下:
./x.go:22:6: cannot use man literal (type man) as type human in assignment:
man does not implement human (sing method has pointer receiver)
说man
没有实现human
,因为sing
是个指针接收者方法。
这里看下飞雪无情的总结:
实体类型以指针接收者实现接口的时候,只有指向这个类型的指针才被认为实现了该接口
如果是值接收者,实体类型的值和指针都可以实现对应的接口;如果是指针接收者,那么只有类型的指针能够实现对应的接口
再看下饶大的解释:
接收者是指针类型的方法,很可能在方法中会对接收者的属性进行更改操作,从而影响接收者;而对于接收者是值类型的方法,在方法中不会对接收者本身产生影响。
所以,当实现了一个接收者是值类型的方法,就可以自动生成一个接收者是对应指针类型的方法,因为两者都不会影响接收者。但是,当实现了一个接收者是指针类型的方法,如果此时自动生成一个接收者是值类型的方法,原本期望对接收者的改变(通过指针实现),现在无法实现,因为值类型会产生一个拷贝,不会真正影响调用者。
平时我们写代码的时候也不用可以记这个,不仅编译器会报错,goland也一样会提示。
参考
https://www.qtmuniao.com/2020/01/06/go-value-pointer-method/
https://qcrao91.gitbook.io/go/interface/zhi-jie-shou-zhe-he-zhi-zhen-jie-shou-zhe-de-qu-bie
https://www.flysnow.org/2017/04/03/go-in-action-go-interface.html