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
,那么您不需要断言,并且可在编译时生成。如果在不需要的情况下使用断言,该怎么办呢?
var x Xer = Foo{}
empty := x.(interface{})
在这种情况下会发生什么?如果有人能帮我澄清一下,那就太好了。
为了扩展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{}
的InterfaceType
被加载到堆栈的第一个值。第35-36行和37-38行放置了x
的制表符和数据值放到堆栈上。然后堆栈就为运行时做好了准备。assertI2E被调用;它只是将底层类型和数据赋值给返回值。编译器知道您将分配给一个空接口,因此调用assertI2E
: I2E
代表interface to Eface (empty interface),因此不需要检查方法。assertI2E
强制的唯一限制是断言值必须是接口。
然而,如果你正在做x.(Xer)
, runtime.assertI2I
将被调用,然后检查是否方法实现接口
我不知道什么是静态接口绑定,什么是动态接口绑定。语言规范从来没有提到过这样的术语。假设您指的是编译时的类型检查和运行时的类型检查。有了这个假设,你所有的例子都是正确的。它可以归结为一个简单的模式(相关语言规范部分的浓缩版本):
- 所有赋值都在编译时进行类型检查。
- 所有类型断言(
.(T)
)在运行时进行类型检查。
这也回答了"在这种情况下会发生什么?"。
也就是说,编译器可以自由地优化它可以在编译时证明类型已知且赋值兼容的情况。但这只是一个不能依赖的实现细节——因为它可能不是其他符合规范的实现的情况。
实际上gc编译器有(IINM)这样的优化,甚至在相反的意义上。它可以说'不可能的类型断言',它可以在编译时证明类型断言将失败。