Invokeinterface 不同,因为接口只在运行时进行类型检查。使用虚方法,您可以静态确定该类型是定义该方法的类的子类型。对于接口,如果不知道值的运行时类型,就不可能确定值是否具有实现该接口的类型。
考虑以下伪代码(注意这在 Java 中是不允许的,但 JVM 允许等效的字节码)
class A
class B extends A implements Foo
A a = new B()
a.fooMethod()
没有办法静态地知道 a 是否实现了 Foo ,因为静态类型 A 没有实现 Foo 但实际的运行时类型 B 实现了。
编辑:上面的示例将被 Java 编译器拒绝,但不会被 JVM 拒绝。您可能想知道为什么 JVM 不只是应用与编译器相同的规则。不同之处在于 JVM 没有关于局部变量的源级类型信息。考虑以下示例,它在 Java 中是允许的。
class A
class B extends A implements Foo
class C extends A implements Foo
Foo x = null;
if (whatever) {
x = new B();
} else {
x = new C();
}
x.fooMethod();
JVM 不知道 x 的预期类型(没有 stackmaptables,直到很久以后才引入),因此它推断 x 的类型为 be A
,但未实现Foo
. 因此,如果它试图静态检查接口作为验证时间,它将拒绝有效的 Java 代码!唯一可行的解决方案是不对接口进行类型检查。
为了安全地检查接口,JVM 必须能够推断出诸如“A 的子类也实现了 Foo”之类的类型,这显然给必须快速高效的东西增加了巨大的复杂性。所以设计师没有走这条路是有道理的。
PS Invokespecial 不仅仅用于构造函数——它也用于私有和超级方法调用。很可能它最初是作为优化的单独指令,因为调用的方法在加载时是已知的,而不是随目标的运行时类型而变化。事实上,它最初被称为invokenonvirtual。