1 回答

TA贡献1833条经验 获得超4个赞
这是绊倒的常见点,特别是对于刚接触更高层次语言背景的人来说。以下是我如何保持直截了当:
首先,让我们确定Go中的“接收器”(例如“方法”)是什么。就像Python一样,一个方法实际上与一种类型相连的事实是语法糖。康德塞尔这个例子:
package main
import "fmt"
type F struct {
i int
}
func (f F)Foo() {
fmt.Println(f.i)
}
func main() {
F.Foo(F{1})
}
尽管可能令人惊讶,但此代码已编译并成功打印出预期的 .这是因为当您在类型上调用接收器时,真正发生的事情是该类型成为接收器的第一个参数。1
真的很快,让我们也回顾一下指针,因为你似乎在评论中倒着说了。因此,需要明确的是:计算机程序中的任何值都存储在内存中,它在内存中的地址也是可以存储在变量中的值。我们将这些变量称为“指向”值的指针。
如果我给一个函数一个值,他们只能在其函数的范围内更改该值。如果该值是内存中的地址,则同样如此。但是,我可以更改该地址的数据,从而影响具有函数外部范围的数据。
package main
import "fmt"
func f(i int) { i = i + 2 }
func pf(i *int) { *i = *i + 2 }
var i = 1
func main() {
f(i)
fmt.Println(i)
pf(&i)
fmt.Println(i)
}
打印输出
1
3
f(i)更改其本地副本,但更改存储在 中的地址处的数据。ipf(&i)i
我为什么经历这一切?因为这就是围棋中的大多数接收器都是指针接收器的原因;因为您不想传递接收器的副本;您实际希望传递接收方的地址,以便它可以在自己的方法中改变自身。
请记住,接收器是语法糖:
func (t *Type)f(arg string)
等效于:
func f(t *Type, arg string)
希望这能清楚地说明为什么指针接收器如此普遍!好了,说到界面方面。
如您所知,接口定义了一组方法。如果类型定义了具有相同签名的方法,则该类型将满足该接口。对于值接收器或指针接收器,情况可能如此。
但是,一个类型不能同时具有具有相同名称的值接收器和指针接收器:
func (t T)F() {}
func (t *T)F() {} //method redeclared: T.F
因此,这意味着类型和指向该类型的指针不能具有相同的接收器;因此,类型或指向类型的指针实现接收器,但不是两者。这一点很容易被忽视,因为go会自动转换。所以这工作正常:
type T struct{}
func (t T)F() {}
func (t *T)PF() {}
func main() {
var t T
t.F()
t.PF()
}
在 中,将自动转换为指针。t.PF()t
但重申一下,类型和指向类型的指针不能同时定义相同的接收器。因此,如果满足接口 ,则不满足,反之亦然。TI*T
话虽如此,并且已经理解了这一点,很容易想出一个简单的规则:当你打算用指针满足接口时,永远不要将指针指向接口。
在代码中,满足 。所以你可以说你的“真的是*A*A'没有任何意义。*AIAIA. Thinking of it like this, you can see that taking the address of an IA that is actually an
将它们放在一起,下面是定义两个接口并链接调用。请注意,虽然指向我的结构的指针可能是满足接口的值,但我永远不需要获取接口的地址。对于 和 接口的使用者来说,它们是类型还是指针接收器都不相关。IFIG
package main
import "fmt"
type IF interface {
G() IG
}
type F struct {
g IG
}
func (f *F)G() IG {
return f.g
}
type IG interface {
G()
}
type G struct{
i int
}
func (g *G)G() {
g.i++
fmt.Println("Gee, ", g.i)
}
func main() {
f := F{&G{1}}
f.G().G()
}
需要指向接口的指针是非常罕见的,因此请确保您认为“接口由指针满足”而不是“指向接口的指针”。
- 1 回答
- 0 关注
- 107 浏览
添加回答
举报