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定义同名方法。。