概述
golang的值接收者和指针接收者的区别
- 方法
- 值接收者和指针接收者
- 两者分别在何时使用
方法
方法能给用户自定义的类型添加新的行为。方法和函数的区别在于方法有一个接收者,给一个函数添加一个接收者,那么它就变成了方法。接收者可以是值接收者,也可以是指针接收者。
我们在调用方法的时候,值类型既可以调用值接收者的方法,也可以调用指针接收者的方法;
指针类型既可以调用指针接收者的方法,也可以调用值接收者的方法。
也就是说,不管方法的接收者是什么类型,该类型的值和指针都可以调用,不必严格符合接收者的类型。下面的一个实例可以验证这个结论:
package main
import "fmt"
type Person struct {
name string
age int
}
func (p Person) howOld() int {
fmt.Printf("internal howOld, addrss is %p n", &p)
return p.age
}
func (p Person) howOld2() int {
fmt.Printf("internal howOld2, addrss is %p n", &p)
return p.age
}
func (p *Person) growUp() {
fmt.Printf("internal growUp, addrss is %p n", p)
p.age += 1
}
func main() {
yuting := Person{
name: "yuting",
age: 18,
}
fmt.Printf("person, addrss is %p n", &yuting)
yuting.howOld()
yuting.howOld2()
yuting.growUp()
fmt.Printf("person, addrss is %p n", &yuting)
fmt.Println("------------- 2 -------------")
yt := &Person{
name: "yuting",
age: 18,
}
fmt.Printf("person, addrss is %p n", yt)
yt.howOld()
yt.howOld2()
yt.growUp()
fmt.Printf("person, addrss is %p n", yt)
}
我们看看这个实例的执行结果:
person, addrss is 0xc00000c040
internal howOld, addrss is 0xc00008a020
internal howOld2, addrss is 0xc00000c060
internal growUp, addrss is 0xc00000c040
person, addrss is 0xc00000c040
------------- 2 -------------
person, addrss is 0xc00000c080
internal howOld, addrss is 0xc00000c0a0
internal howOld2, addrss is 0xc00008a040
internal growUp, addrss is 0xc00000c080
person, addrss is 0xc00000c080
从打印出的地址可以看出:
- 对于值接收者,如果调用者也是值对象,那么会将调用者的值拷贝一份,并执行方法,方法的调用不会影响到调用者值。 如果调用者是指针对象,那么会解引用指针对象为值,然后将解引的对象拷贝一份,然后 执行方法。
- 对于指针接收者,如果调用者是值对象,会使用值的引用来调用方法,上例中,
yuting.growUp()
实际上是 、(&yuting).growUp()
, 所以传入指针接收者方法的对象地址和 调用者地址一样。如果调用者是指针对象,实际上也是“传值”,方法里的操作会影响到调用者,类似于指针传参,拷贝了一份指针,但是指针指向同一个对象。
用一个表格来表示:
值接受者 | 指针接收者 | |
---|---|---|
值调用者 | 方法会使用调用者的一个副本,类似于“传值“ | 使用值的引用来调用方法,上例中,yuting.growUp() 实际上是 (&yuting).growUp() |
指针调用者 | 指针被解引用为值,上例中,yt.howOld() 实际上是 (*yt).howOld() | 实际上也是“传值”,方法里的操作会影响到调用者,类似于指针传参,拷贝了一份指针 |
值接收者和指针接收者
前面说过,不管接收者类型是值类型还是指针类型,都可以通过值类型或指针类型调用,这里面实际上通过语法糖起作用的。
先说结论:实现了接收者是值类型的方法,相当于自动实现了接收者是指针类型的方法;而实现了接收者是指针类型的方法,不会自动生成对应接收者是值类型的方法。
先来看个例子:
package main
import "fmt"
type coder interface {
howOld() int
howOld2() int
growUp()
}
type Gopher struct {
name string
age int
}
func (p Gopher) howOld() int {
fmt.Printf("internal howOld, addrss is %p n", &p)
return p.age
}
func (p Gopher) howOld2() int {
fmt.Printf("internal howOld2, addrss is %p n", &p)
return p.age
}
func (p *Gopher) growUp() {
fmt.Printf("internal growUp, addrss is %p n", p)
p.age += 1
}
func main() {
fmt.Println("------------- 1 -------------")
var yt coder = &Gopher{
name: "yuting",
age: 18,
}
fmt.Printf("person, addrss is %p n", yt)
yt.howOld()
yt.howOld2()
yt.growUp()
fmt.Printf("person, addrss is %p n", yt)
fmt.Println("------------- 2 -------------")
var yt coder = Gopher{
name: "yuting",
age: 18,
}
//fmt.Printf("person, addrss is %p n", &yuting)
//yuting.howOld()
//yuting.howOld2()
//yuting.growUp()
//fmt.Printf("person, addrss is %p n", &yuting)
}
上面定义了一个接口:
type coder interface {
howOld() int
howOld2() int
growUp()
}
定义了结构体:Gopher,它实现了两个方法,两个值接收者,一个指针接收者。最后,我们在 main 函数里通过接口类型的变量(Gopher指针)调用了定义的两个函数。运行正常。
但是如果我们把 coder interface 变量的值改成 Gopher 实例,就会编译失败,报错:
cannot use Gopher literal (type Gopher) as type coder in assignment:
Gopher does not implement coder (growUp method has pointer receiver)
提示显示:Gopher 对象没有实现coder 接口的 growUp 方法,因为growUp有一个指针接收者。
简单的说就是,*Gopher 实现了coder, 但是Gopher没有实现 coder。
有一个简单的解释:接收者是指针类型的方法,很可能在方法中会对接收者的属性进行更改操作,从而影响接收者;而对于接收者是值类型的方法,在方法中不会对接收者本身产生影响。所以,当实现了一个接收者是值类型的方法,就可以自动生成一个接收者是对应指针类型的方法,因为两者都不会影响接收者。但是,当实现了一个接收者是指针类型的方法,如果此时自动生成一个接收者是值类型的方法,原本期望对接收者的改变(通过指针实现),现在无法实现,因为值类型会产生一个拷贝,不会真正影响调用者。
结论就是:如果实现了接收者是值类型的方法,会隐含地也实现了接收者是指针类型的方法。
两者分别在何时使用
如果方法的接收者是值类型,无论调用者是对象还是对象指针,修改的都是对象的副本,不影响调用者;如果方法的接收者是指针类型,则调用者修改的是指针指向的对象本身。
使用指针作为方法的接收者的理由:
- 方法能够修改接收者指向的值。
- 避免在每次调用方法时复制该值,在值的类型为大型结构体时,这样做会更加高效。
最后
以上就是粗犷帆布鞋为你收集整理的golang的值接收者和指针接收者的区别方法值接收者和指针接收者两者分别在何时使用的全部内容,希望文章能够帮你解决golang的值接收者和指针接收者的区别方法值接收者和指针接收者两者分别在何时使用所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复