Go使用动态和静态绑定。根据我的理解,如果你需要使用类型断言,那么它是动态的。我想验证我的假设。
type Xer interface {
X()
}
type XYer interface {
Xer
Y()
}
type Foo struct{}
func (Foo) X() { println("Foo#X()") }
func (Foo) Y() { println("Foo#Y()") }
假设:
foo := Foo{}
// static: Foo -> XYer
var xy XYer = foo
// static: XYer -> Xer
var x Xer = xy
// static: Xer -> interface{}
var empty interface{} = x
// dynamic: interface{} -> XYer
xy2 := empty.(XYer)
// dynamic: XYer -> Foo
foo2 := xy2.(Foo)
因此从type A
转换时 - > interface B
,如果A
满足B
,则您不需要断言,并且可以在编译时生成itable。如果您在不需要的情况下使用断言,那么情况如何:
var x Xer = Foo{}
empty := x.(interface{})
在这种情况下会发生什么?如果有人能为我澄清这个很好的话。
答案 0 :(得分:5)
我不知道接口的静态绑定或接口的动态绑定是什么。语言规范从未提及过这样的术语。我假设您的意思是在编译时进行类型检查并在运行时进行类型检查。有了这些假设,您的所有示例都是AFAICS,都是正确的。它归结为一个简单的模式(相关语言规范部分的精简版):
.(T)
)都在运行时进行类型检查。这也回答了“在这种情况下会发生什么?”。
也就是说,编译器可以自由地优化它可以在编译时证明类型已知且赋值兼容的情况。但这只是一个人们不能依赖的实现细节 - 因为它可能不是其他符合规范的实现的情况。
实际上gc编译器有(IINM)这样的优化,甚至是相反的意义。它可以说'不可能的类型断言',它可以在编译时证明类型断言将失败。
答案 1 :(得分:5)
为了扩展jnml的答案,6g
生成了一个类型断言。
empty := x.(interface{})
扩展为:
0034 (dumb.go:19) MOVQ $type.interface {}+0(SB),(SP)
0035 (dumb.go:19) LEAQ 8(SP),BX
0036 (dumb.go:19) MOVQ x+-32(SP),BP
0037 (dumb.go:19) MOVQ BP,(BX)
0038 (dumb.go:19) MOVQ x+-24(SP),BP
0039 (dumb.go:19) MOVQ BP,8(BX)
0040 (dumb.go:19) CALL ,runtime.assertI2E+0(SB)
0041 (dumb.go:19) MOVQ 24(SP),BX
0042 (dumb.go:19) MOVQ BX,empty+-16(SP)
0043 (dumb.go:19) MOVQ 32(SP),BX
0044 (dumb.go:19) MOVQ BX,empty+-8(SP)
为了澄清这里发生的事情,在第34行中,interface{}
的{{3}}被加载到
堆栈的第一个值。第35-36行和第37-38行放置x
assertI2E
到堆栈上。然后堆栈准备好调用InterfaceType
,
它只是将基础类型和数据分配给返回值。编译器知道这一点
您正在分配一个空接口,因此调用I2E
:assertI2E
代表接口tab and data values(空接口),因此不需要检查方法。唯一限制x.(Xer)
强制执行的是
断言的值必须是接口。
但是,如果您正在执行{{1}},则会调用runtime.assertI2E,然后调用Eface 这些方法实现了接口。