golang 方法接收者
- [定义]: golang的方法(Method)是一个带有receiver的函数Function,Receiver是一个特定的struct类型,当你将函数Function附加到该receiver, 这个方法Method就能获取该receiver的属性和其他方法。
- [面向对象]: golang方法Method允许你在类型上定义函数,是一个面向对象的行为代码, 这也有一些益处:同一个package可以有相同的方法名, 但是函数Function却不行。
func (receiver receiver_type) some_func_name(arguments) return_values
从应用上讲,方法接受者分为值接收者,指针接收者,初级golang学者可能看过这两个接收者实际表现, 但是一直很混淆,很难记忆。
本次我们使用地址空间的角度来剖析实质,强化记忆。
值类型方法接收者
值接受者: receiver是struct等值类型。
下面定义了值类型接受者Person
, 尝试使用Person{}
, &Person{}
去调用接受者函数。
package main import "fmt" type Person struct { name string age int } func (p Person) say() { fmt.Printf("I (%p) ma %s, %d years old \n",&p, p.name,p.age) } func (p Person) older(){ // 值类型方法接受者: 接受者是原类型值的副本 p.age = p.age +1 fmt.Printf("I (%p) am %s, %d years old\n", &p, p.name,p.age) } func main() { p1 := Person{name: "zhangsan", age: 20} p1.older() p1.say() fmt.Printf("I (%p) am %s, %d years old\n",&p1, p1.name,p1.age) p2 := &Person{ name: "sili", age: 20} p2.older() // 即使定义的是值类型接受者, 指针类型依旧可以使用,但我们传递进去的还是值类型的副本 p2.say() fmt.Printf("I (%p) am %s, %d years old\n",p2, p2.name,p2.age) }
尝试改变p1=Person{},p2=&Person{}的字段值:
I (0xc000098078) am zhangsan, 21 years old I (0xc000098090) ma zhangsan, 20 years old I (0xc000098060) am zhangsan, 20 years old I (0xc0000980c0) am sili, 21 years old I (0xc0000980d8) ma sili, 20 years old I (0xc0000980a8) am sili, 20 years old
p1=Person{} 未能修改原p1的字段值; p2=&Person{}也未能修改原p2的字段值。
- 通过Person{}值去调用函数, 传入函数的是原值的副本, 这里通过第一行和第三行的
%p
印证 (%p:输出地址值, 这两个非同一地址)。 - 即使定义的是值类型接收者,指针类型依旧可以调用函数, 但是传递进去的还是值类型的副本。
带来的效果是:对值类型接收者内的字段操作,并不影响原调用者。
指针类型接受者
方法接收者也可以定义在指针上,任何尝试对指针接收者的修改,会体现到调用者。
package main import "fmt" type Person struct{ name string age int } func (p Person) say(){ fmt.Printf("I (%p) am %s, %d years old\n", &p, p.name,p.age) } func (p *Person) older(){ // 指针接受者,传递函数内部的是原类型值(指针), 函数内的操作会体现到原指针指向的空间 p.age = p.age +1 fmt.Printf("I (%p) am %s, %d years old\n", p, p.name,p.age) } func main() { p1 := Person{"zhangsan",20} p1.older() // 虽然定义的是指针接受者,但是值类型依旧可以使用,但是会隐式传入指针值 p1.say() fmt.Printf("I (%p) am %s, %d years old\n", &p1, p1.name,p1.age) p2:= &Person{"sili",20} p2.older() p2.say() fmt.Printf("I (%p) am %s, %d years old\n", p2, p2.name,p2.age) }
尝试改变p1= Person{}, p2=&Person{}字段值
I (0xc000098060) am zhangsan, 21 years old I (0xc000098078) am zhangsan, 21 years old I (0xc000098060) am zhangsan, 21 years old I (0xc000098090) am sili, 21 years old I (0xc0000980a8) am sili, 21 years old I (0xc000098090) am sili, 21 years old
p1=Person{} 成功修改字段值,p2=&Person{}也成功修改字段值。
- 通过p1也可以调用指针函数接收者, 但是实际会隐式传递指针值。
- 指针接收者,入参是原指针值,函数内的操作会体现到原调用者。
带来的效果: 任何对指针接收者的修改会体现到 原调用者。
什么时候使用指针接收者
- 需要对接受者的变更能体现到原调用者
- 当struct占用很大内存,最好使用指针接受者,否则每次调用接受者函数 都会形成struct的大副本
**
有些情况下,以值或指针做为接收者都可以,但是考虑到代码一致性,建议都使用指针做为接收者。
不管你使用哪种方法定义方法,指针实例对象、值实例对象都可以直接调用,而没有什么约束。这一点Go语言做得非常好。
**
golang方法的几种姿势
接上例子:
- 将接收者函数当扩展函数
Person.say(p1) (*Person).older(p2)
依旧是 值类型/指针类型方法接收者的效果。
I (0xc0000040d8) am zhangsan, 21 years old I (0xc0000040a8) am sili, 22 years old
这种姿势相对于面向对象的接收者不常见。
- golang 方法链条
func (p Person) printName() Person{ fmt.Printf("Name:%s", p.Name) return p }
- Non_struct类型golang方法
type myFloat float64 func (m myFloat) ceil() float64 { return math.Ceil(float64(m)) }
以上是有态度的马甲记录的有关golang 方法接收者的全部用法,通过%p
,我们探究了值类型/指针接收者的调用原理。
本文来自博客园,作者:{有态度的马甲},转载请注明原文链接:https://www.cnblogs.com/JulianHuang/p/16330427.html
欢迎关注我的原创技术、职场公众号, 加好友谈天说地,一起进化
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
2021-05-31 String 是一个奇怪的引用类型