当前位置: 首页 > 科技观察

再说说Golang的方法接收者

时间:2023-03-14 14:19:00 科技观察

【定义】:Golang的方法(Method)是一个带有接收者的函数Function,Receiver是一个特定的struct类型,当你把函数Function附加到接收者上时,这个方法Method就可以得到接收者的属性和其他方法。【面向对象】:golang方法Method允许你在类型上定义函数,是一种面向对象的行为代码,它也有一些好处:同一个包可以有相同的方法名,但是函数Function不能。func(receiverreceiver_type)some_func_name(arguments)return_values从应用上来说,方法接收者分为值接收者/指针接收者。初级golang学者可能看过这两个receiver的实际表现,但一直比较混乱,难以记忆。这次我们从地址空间的角度来剖析本质,加强记忆。值类型方法receiverValuereceiver:receiver是一个struct等价类型。下面定义了值类型接收者Person,尝试使用Person{}、&Person{}来调用接收者函数。packagemainimport"fmt"typePersonstruct{namestringageint}func(pPerson)say(){fmt.Printf("I(%p)ma%s,%d岁\n",&p,p.name,p.age)}func(pPerson)older(){//值类型方法接收者:接收者是原始类型值的副本p.age=p.age+1fmt.Printf("I(%p)am%s,%d岁\n",&p,p.name,p.age)}funcmain(){p1:=Person{name:"zhangsan",age:20}p1.older()p1.say()fmt.Printf("I(%p)am%s,%d岁\n",&p1,p1.name,p1.age)p2:=&Person{name:"sili",age:20}p2.older()//即使定义了值类型receiver,仍然可以使用指针类型,但我们传入的仍然是值类型的副本p2.say()fmt.Printf("I(%p)am%s,%dyearsold\n",p2,p2.name,p2.age)}尝试改变p1=Person{},p2=&Person{}的字段值:I(0xc000098078)amzhangsan,21岁我(0xc000098090)马长三,20岁我(0xc000098060)我是张三,20岁我(0xc0000980c0)我是斯里,21岁我(0xc0000980d8)马斯里,20岁我(0xc0000980a8)08岁斯里,090a808amsili,20oftheoriginalPerson1=yearscannotbemodified}字段值;p2=&Person{}也未能修改p2的原始字段值。通过Person{}的值调用函数,将原值的副本传入函数,第一行和第三行%p确认(%p:输出地址值,这两个地址不是相同)。即使定义了值类型接收者,指针类型仍然可以调用函数,但传入的仍然是值类型的副本。效果是:值类型接收者中的字段操作不影响原来的调用者。指针类型接收器方法接收器也可以定义在指针上,任何修改指针接收器的尝试都会反映在调用者中。packagemainimport"fmt"typePersonstruct{namestringageint}func(pPerson)say(){fmt.Printf("I(%p)am%s,%dyearsold\n",&p,p.name,p.age)}func(p*Person)older(){//指针接收者,函数内部传递的是原始类型值(指针),函数中的操作会体现在原始指针指向的空间p.age=p.age+1fmt.Printf("I(%p)am%s,%dyearsold\n",p,p.name,p.age)}funcmain(){p1:=Person{"zhangsan",20}p1.older()//虽然定义了指针接收者,但仍然可以使用值类型,但是指针值会隐式传入p1.say()中fmt.Printf("I(%p)am%s,%d岁\n",&p1,p1.name,p1.age)p2:=&Person{"sili",20}p2.older()p2.say()fmt.Printf("I(%p)am%s,%dyearsold\n",p2,p2.name,p2.age)}尝试改变p1=Person{},p2=&Person{}字段值I(0xc000098060)am张三,21岁我(0xc000098078)我是张三,21岁我(0xc000098060)我是张三,21岁我(0xc000098090)我是斯里,21yearsoldI(0xc0000980a8)amsili,21yearsoldI(0xc000098090)amsili,21yearsoldp1=Person{}成功修改字符串段值,p2=&Person{}也成功修改字段值。也可以通过p1调用指针函数receiver,但会隐式传递指针值。指针接收者,入参为原来的指针值,函数中的操作会在原来的调用者中体现出来。效果:对指针接收器的任何修改都会反映在原始调用者中。当使用指针接收器时,对接收器的更改需要反映在原始调用者中。当struct占用内存较多时,最好使用指针receiver,否则每次调用receiver函数都会形成一个struct的大拷贝。这个姿势后面再举个例子:1.使用接收函数作为扩展函数的效果Person.say(p1)(*Person).older(p2)仍然是值类型/指针类型的方法接收者:I(0xc0000040d8)amzhangsan,21yearsoldI(0xc0000040a8)amsili,22years这种姿势对于面向对象的接收者来说并不常见。2.形成一个golang方法链func(pPerson)printName()Person{fmt.Printf("Name:%s",p.Name)returnp}3.Non_struct类型golang方法typemyFloatfloat64func(mmyFloat)ceil()float64{返回数学。天花板(float64(m))}