给出以下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 tourGuide
类型的变量或指向tourGuide t1
的指针调用struct t2
的两个方法。换句话说,我可以使用T
或T
类型的变量调用*T
接收方的方法。同样,我可以使用*T
类型的变量(如果T
为addressable)或T
使用*T
接收方调用方法。我知道编译器在这里处理差异(参见我在代码中的注释)。
然而,当我们实现接口时,事情会发生变化。在上面的代码中,类型greeter
接口的变量可以从指向tourGuide
的指针分配,但不能从tourGuide
指定。
谁能告诉我为什么会这样呢?为什么我能够拨打t1.hello()
和t1.goodbye()
,但某种方式t1
对于greeter
接口来说还不够?
答案 0 :(得分:7)
如果方法具有指针接收器,则只能将指针值用作接收器值。因此,要在某个值上调用此方法,值本身必须是指针,或者必须可以获取其地址(用作接收器)。
如果您有一个变量,例如,它是addressable,因此可以获取其地址并将其用作接收器。规范允许你这样做,这会自动发生。
接口中包含的值不可寻址。创建接口值时,将复制包含在接口中的值。因此无法取其地址。理论上你可以允许获取副本的地址,但这将是(甚至)更混乱的来源,而不是它提供的好处,因为地址指向副本,而带有指针接收器的方法只能修改副本而不是原来的。
请参阅此答案,详细说明/证明在创建界面值时复制值:How can a slice contain itself?
答案 1 :(得分:3)
如果你有一个指向结构的指针,那么go将允许你访问结构及其函数的属性,这些属性具有值类型接收器(与指针接收器相对应),而不必取消引用指针,但这仅适用于一级指针,请参阅下面的代码,我将t2转换为指向tourguide
指针的指针,此时我需要显式取消引用它,使其返回指向tourguide
的指针。想想结构指针的第一级作为一个特殊情况,允许你使用合成糖来访问值类型属性和函数,以节省你不得不经常手动取消引用变量。
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 tmpT2 *tourGuide = &tourGuide{"Smith"}
var t2 **tourGuide = &tmpT2
(*t2).hello() // Hello Smith
(*t2).goodbye() // Goodbye Smith (same as (*t2).hello())
//illegal: t1 is not assignable to g1 (why?)
//var g1 greeter = t1
//now this is illegal too
//var g2 greeter = t2
var g3 greeter = (*t2)
g3.hello() // Hello Smith
g3.goodbye() // Goodbye Smith
}
答案 2 :(得分:0)
The answer here解释了为什么Go阻止您获取存储在接口中的值的地址。
tl; dr ,因为指向 A 的指针指向类型 A B 的值时,界面中的将失效。