golang 值接收者和指针接收者
代码示例
先看个代码:
package main import ( "fmt" "testing" ) func TestValRecv(t *testing.T) { var a animal = dog{"五红犬"} a.eat() a.sleep() var b animal = &dog{"五黑犬"} b.eat() b.sleep() } func TestPtrRecv(t *testing.T) { var a animal = &cat{"蓝猫"} a.eat() a.sleep() var b animal = cat{"胖橘"} // sleep only a ptr recv,出错 b.eat() b.sleep() } type animal interface { eat() sleep() } type dog struct { name string } func (d dog) eat() { fmt.Printf("%s eat...\n", d.name) } func (d dog) sleep() { fmt.Printf("%s sleep...\n", d.name) } type cat struct { name string } func (d cat) eat() { fmt.Printf("%s eat...\n", d.name) } func (d *cat) sleep() { fmt.Printf("%s sleep...\n", d.name) }
先说结论:
- 值接收者,默认实现了值接收和指针接收方法,源对象的一个复制或者拷贝
- 指针接收,只实现指针接收,重点是修改原对象
所以上面 cat 一个是 eat() 值接收方法,实现了二者,而 sleep() 指针接收,只实现了指针接收,未完全实现接口 animal 的两个方法。
如果不涉及接口,二者都可以,但涉及到接口涉及具体实现问题。
对于接口
结论:
实现了接收者是值类型的接口方法,相当于自动实现了接收者是指针类型的方法;
实现了接收者是指针类型的接口方法,不会自动生成对应接收者是值类型的方法。
简单的解释
接收者是指针类型的方法,很可能在方法中会对接收者的属性进行更改操作,从而影响接收者;而对于接收者是值类型的方法,在方法中不会对接收者本身产生影响。
所以,当实现了一个接收者是值类型的方法,就可以自动生成一个接收者是对应指针类型的方法,因为两者都不会影响接收者。但是,当实现了一个接收者是指针类型的方法,如果此时自动生成一个接收者是值类型的方法,原
本期望对接收者的改变(通过指针实现),现在无法实现,因为值类型会产生一个拷贝,不会真正影响调用者。
两者分别在何时使用
如果方法的接收者是值类型,无论调用者是对象还是对象指针,修改的都是对象的副本,不影响调用者;如果方法的接收者是指针类型,则调用者修改的是指针指向的对象本身。
使用指针作为方法的接收者的理由:
方法能够修改接收者指向的值。避免在每次调用方法时复制该值,在值的类型为大型结构体时,这样做会更加高效。
使用值接收者还是指针接收者,不是由该方法是否修改了调用者(也就是接收者)来决定,而是应该基于该类型的本质。
如果类型具备“原始的本质”,也就是说它的成员都是由 Go 语言里内置的原始类型,如字符串,整型值等,那就定义值接收者类型的方法。像内置的引用类型,如 slice,map,interface,channel,这些类型比较特殊,声明他们的时候,实际上是创建了一个 header, 对于他们也是直接定义值接收者类型的方法。这样,调用函数时,是直接 copy 了这些类型的 header,而 header 本身就是为复制设计的。
如果类型具备非原始的本质,不能被安全地复制,这种类型总是应该被共享,那就定义指针接收者的方法。比如 go 源码里的文件结构体(struct File)就不应该被复制,应该只有一份实体。
参考:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
2022-07-22 go语言常用命令汇总