内容均来自 https://www.bilibili.com/video/BV1Eb4y1F7b9
https://juejin.cn/post/6963476381728702501
什么时候要使用指针接收器?
1.A很大,因为Go语言在执行函数时会进行参数的拷贝,拷贝一个大的对象和拷贝一个
指针相比代价肯定要大。
2.当方法中要修改A本身的数据时。同样是因为拷贝的原因,对于拷贝对象的修改不
会影响原对象
type I1 interface{
F1()
}
type I2 interface{
F1()
F2()
}
type T1 struct{
d int
}
func(t T1)F1(){
fmt.Printin("F1",t.d)
}
func(t *T1)F2(){
fmt.Printin("F2",t.d)
}
//OK
var _ I1=T1
//OK
var _ I1=&T1
//OK
var _ I2=&T1
//Not OK
var _ I2=T1
如果一个类型A实现了某方法,就相当于*A也实现了该方法。
反过来不成立。
前提A本身不能是指针类型,如果*A实现了某方法,对于**A没有影响。
所以A实现了的接口,*A一定也实现了。
反过来不一定。
A是如何调用*A的方法的 (互相调用)
可以认为是golang中的一种语法糖
func (s *mystruct)Read(data []byte)(int,error){
return 0,nil
}
如果有了上面的不允许再定义下面的
func (s myStruct)Read(data []byte)(int,error){
return 0,nil
}
不管在上面是谁实现了Read方法,下面的两段代码都是0K的
a:=mystruct{}
a.Read(nil)
b:=&mystruct{}
b.Read(nil)
type A struct
d int
}
func(a *A)F1(× int)(
a.d+=x
}
func main(){
a:=A{d:10}
a.F1(5) //等于(&a).F1(5)
fmt.Println(a.d)
}
type A struct{
d int
}
func(a A)F2(x int){
a.d+=x
}
func main(){
a:=&A{d:10}
a.F2(5) //等于(*a).F2(5)
fmt.Println(a.d)//结果还是10
}
*A拥有A的所有方法。对A的方法做了包装,先做指针解引用,再调用A的方法。
*A的变量可以直接调用A的方法,处理逻辑同上
可以理解为语法糖,也可以理解为对于上面的包装后的方法的调用。
A没有A的方法
A的变量可以直接调用A的方法,处理逻辑是先取指针,再调用*A的方法。
只能理解为是语法糖。
https://stackoverflow.com/questions/48790663/why-value-stored-in-an-interface-is-not-addressable-in-golang
https://go.dev/doc/faq#different_method_sets
为什么要让*A拥有A的所有方法?
可能的原因:
1.*A调用A的方法的过程总是安全的。
2.*A调用A的方法的结果总是符合预期的,而且是有益的。
不太可能是因为接口实现的需要而实现这样的特性。
1.这是在Go的Spec里面设定的行为,正常的顺序是Spec在前,实现在后。
2.Go语言在设计层面并没有对接口实现做设定,所以不允许取接口的地址
为什么不让A拥有*A的方法?
看一个例子,假设A拥有*A的方法,下面的代码将是正确的,但是结果不符合预期
var buf bytes.Buffer
io.Copy(buf,os.Stdin)//将数据拷贝到buf里面
//相关代码
func Copy(dst Writer,src Reader)(written int64,err error){
return copyBuffer(dst,src,nil)
}
type Writer interface{
Write(p []byte)(n int,err error)
}
func (b *Buffer)Write(p []byte)(n int,err error){
}
如果让A拥有A的方法,也就是让A实现A所实现的接口,此时将A作为接口类型的变量使用,与*A的行为是有差异的,而且是我们不想要的行为。