给出以下 Go 代码示例:
package main import "fmt" type greeter interface { hello() goodbye() } type tourGuide struct { name string } func (t tourGuide) hello() { fmt.Println("Hello", t.name) } func (t *tourGuide) goodbye() { fmt.Println("Goodbye", t.name) } func main() { var t1 tourGuide = tourGuide{"James"} t1.hello() // Hello James t1.goodbye() // Goodbye James (same as (&t1).goodbye()) var t2 *tourGuide = &tourGuide{"Smith"} t2.hello() // Hello Smith t2.goodbye() // Goodbye Smith (same as (*t2).hello()) // illegal: t1 is not assignable to g1 (why?) // var g1 greeter = t1 var g2 greeter = t2 g2.hello() // Hello Smith g2.goodbye() // Goodbye Smith }
在上面的代码中,您可以使用tourGuide类型的变量(t1)或指向tourGuide的指针(t2)调用structtourGuide的两个方法。然而,当实现接口时,情况就会发生变化。 greeter 接口类型的变量可从指向tourGuide 的指针分配,但不能从tourGuide 值分配。为什么会出现这种情况?
要理解原因,了解接收器类型和值语义的概念很重要。 Go 中的方法可以有一个值接收器或一个指针接收器。值接收器方法在值的副本上调用,而指针接收器方法在原始值上调用。
复制值时,它会创建原始值的新独立副本。对副本所做的任何修改都不会影响原始值。这称为值语义。
在tourGuide 示例中,hello 方法有一个值接收器,而goodbye 方法有一个指针接收器。当您调用 t1.hello() 时,会创建 t1 的副本,并在该副本上调用 hello 方法。当你调用 t1.goodbye() 时,goodbye 方法会在原始 t1 值上调用。
Go 中的接口是通过值语义实现的。这意味着当将值分配给接口时,会创建该值的副本。对于指针接收器方法,这意味着该值的副本没有地址,因此无法在其上调用该方法。
例如,当您将 t1 分配给 g1 时(其中是一个接口值)在以下代码行中:
// illegal: t1 is not assignable to g1 (why?) // var g1 greeter = t1
创建了t1的副本,并在副本上实现了hello和goodbye方法。但是,由于副本没有地址,因此无法对其调用 goodbye 方法。
相反,当您在以下代码行中将 t2 分配给 g2 时:
var g2 greeter = t2
创建t2(指向tourGuide的指针)的副本,并在该指针的副本上实现hello和goodbye方法。在这种情况下,指针的副本有一个地址,因此可以在其上调用 goodbye 方法。
综上所述,greeter 接口类型的变量是可赋值的原因从指向tourGuide的指针而不是从tourGuide值是因为接口是通过值语义实现的。当将值分配给接口时,会创建该值的副本,如果接口的方法有指针接收器,则无法在副本上调用它们,因为副本没有地址。
以上是为什么 Go 的 `greeter` 接口不能分配一个 `tourGuide` 值,但可以分配一个指向 `tourGuide` 的指针?的详细内容。更多信息请关注PHP中文网其他相关文章!