Golang 值接收者和指针接收者

package main

import "fmt"

type PersonA struct {
	name string
}

func (p *PersonA) Speak(){
	fmt.Println("person speak")
}

func (p PersonA) Walk(){
	fmt.Println("person walk")
}

func main(){
	pa := PersonA{name:"sandy"}
	pb := &PersonA{"mandy"}
	pa.Speak()
	pa.Walk()
	fmt.Println("########################")
	pb.Speak()
	pb.Walk()
}

pa 能正常调用 walk()   pb能正常调用 Speak() 这没有什么好说的。。。。

pa 能正常调用指针接收者定义的Speak()   背后是编译器做的手脚

pb 能调用值接收者定义的Walk()  也是编译器背后做的手脚

类型不相同 可以调用

当类型和方法的接收者类型不同时,编译器会做一些操作:

值类型调用指针接收者方法时 pa.Speak()   实际是(&pa).Speak()

指针类型调用值接收者方法时 pb.Walk()   实际上是 (*pb).Walk()

类型不同 不可以调用

不能调用有以下2中情况

1:值类型不能取地址的时候

func NewPerson1() (p PersonA){
	return PersonA{"sandy"}
}

func NewPerson2() (p *PersonA){
	return &PersonA{"sandy"}
}
func main(){
    //NewPerson1().Speak()
	NewPerson1().Walk()

	NewPerson2().Speak()
	NewPerson2().Walk()
}
NewPerson1().Speak()会以下报错 
# command-line-arguments
.\test_struct2.go:35:14: cannot call pointer method on NewPerson1()
.\test_struct2.go:35:14: cannot take the address of NewPerson1()

只能分2步,p := NewPerson1() p.Speak() 

2:以指针接收者实现接口的时候

type AAAA interface {
	Speak()
	Walk()
}

func main(){
    var a AAAA = &PersonA{"name":"sandy"}
    a.Speak()
    a.Walk()
}
var a AAAA = PersonA{name:"sandy"}  这么干是行不通的。。。。

T和*T各有各的方法集。。

func (t T)A(){}

func (pt *T)B(){}

T的方法集里边 全部都是有明确定义的接收者是T类型的方法,如func(t T)A()

编译器会为接收者为值类型的方法生成接收者为指针类型的方法,也就是所谓的“包装方法"

*T除了有明确定义的接受者是*T类型的方法,如func(pt *T)B()还有编译器自动生成的包装方法,如 func(pt *T)A()   它就是 编译器对 func(t T)A()同名方法的包装。

为啥编译器要为接收者为T的方法 包装出来一个接收者为*T的同名方法尼??

估计很多人都猜的是 为了方便指针调用,因为  pt.A() 能正常的调用嘛,其实不是的。。

pt.A() 只是一种语法糖,背后其实是 (*pt).A(),经过验证这种调用方式编译器会在调用端进行指针解引用 A(*pt),并不会调用这里边的包装方法。。

编译器生成包装方法 其实是为了支持接口。。。

设计到方法的接口  肯定是个非空接口了,

//非空接口
type iface struct {
    tab  *itab
    data unsafe.Pointer
}
type itab struct {
    inter  *interfacetype
    _type  *_type
    link   *itab
    hash   uint32 // copy of _type.hash. Used for type switches.
    bad    bool   // type does not implement interface
    inhash bool   // has this itab been added to hash?
    unused [2]byte
    fun    [1]uintptr // variable sized
}
//空接口
type eface struct {
    _type *_type
    data  unsafe.Pointer
}

非空接口包含2个指针,一个和类型原数据相关,一个和接口装载的数据相关,虽然有数据指针,但是不能像前边的语法糖一样 通过指针解引用去调用值接收者的方法。。

type I interface {
	A()
}
type T struct {
}
func (t T)A(){
	fmt.Println("A()")
}
var i I
i.A()  //这个调用是不允许的

至于原因, 方法的接受者是方法的第一个参数,就像前边:pt.A() ---->A(*pt),go中函数参数是通过栈来传递的,如果参数是指针类型,那就很好实现了,平台确定了指针的大小也就确定了,如果是值类型就要有明确的值类型,编译器才能知道这个参数在栈上占用多大的空间,而对于接口编译期间它不知道具体要装载哪一类数据,所以编译器也不能生成对应的指令来解引用,总而言之接口不能直接使用接收者为值类型的方法,面对这个问题,编译器就采取了:为接收者是值类型的方法生成接收者是指针类型的同名方法这一解决方案,因此如果我们在给T和*T定义了同名方法就有可能和编译器生成的包装方法冲突,所以go就不让T和*T定义同名方法。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值