反汇编一些Java 8代码我发现invokestatic
接口中的一些静态方法调用(特别是这个java.util.function.Function.identity()
)在const pool中使用了InterfaceMethodRef; 这是javap -s -c -v p
告诉我的:
15: invokestatic #66 // InterfaceMethod java/util/function/Function.identity:()Ljava/util/function/Function;
根据JVM 8规范,这是不可能的,当我在带有Java 7(major version=51
)版本的classfile中使用此指令时,它已在此指令上抛出VerifyError.
但是,当我将主要版本更改为时52
,它开始像魅力一样工作.请注意,我在Oracle JDK 1.8.0_60上运行.我想知道为什么需要进行这种更改(调用的方法是静态链接的,不是吗?)以及是否记录在任何地方.
好吧,在Java 8之前static
,interface
s中的方法是不允许的,所以很明显,无论在Java 8中如何实现,在任何尝试在先前版本或具有旧版本的类文件中使用它们都注定要失败.
在Java 8之前,我们有以下两个规则:
CONSTANT_Methodref_info
结构的class_index项必须是类类型,而不是接口类型.
CONSTANT_InterfaceMethodref_info
结构的class_index项必须是接口类型.
(见JVMSpec7§4.4.2)
invokestatic
必须引用CONSTANT_Methodref_info
条目的方法描述符
(见JVMSpec7§6.5)
该索引处的运行时常量池项必须是对方法(第5.1节)的符号引用,该方法提供方法的名称和描述符(第4.3.3节)以及对其中的类的符号引用.方法是找到的.
可能看起来并不清楚"对方法的符号引用"会排除接口方法,但如果没有这种假设,我们根本不会对Java 8的行为产生任何影响.通过与Java 8的JVM规范进行比较或考虑到接口方法总是隐含为非,它也将变得更加清晰static
.
显而易见的是,为了static
在interface
s中添加对方法的支持,通过invokestatic
至少一个规则必须改变.
如果我们看一下规则,他们的措辞,我们看到,第一个是,而在第二个相当清楚,它不是完全明显,"象征性的参考方法"指的是CONSTANT_Methodref_info
条目和排除一个"符号引用的接口方法".决定改变该规则并同时使措辞更清晰:
(JVMSpec8§6.5):
该索引处的运行时常量池项必须是对方法或接口方法(第5.1节)的符号引用,它提供方法的名称和描述符(第4.3.3节)以及对方法的符号引用.要在其中找到方法的类或接口.
现在很清楚,invokestatic
可能会引用接口方法,因此不需要触及第一条规则,也没有触及它.但请注意,第一条规则从未强制接口方法为非static
.它只是关于声明类型是否是一个interface
.
这显然减少之间的区别价值CONSTANT_Methodref_info
和CONSTANT_InterfaceMethodref_info
但这是不可避免的.如果第一条规则被放宽了,它也会软化这种区别.
但是有一个强有力的理由改变调用方面:由于default
方法的引入,现在有可能default
通过invokespecial
像abstract
之前的其他重写非方法一样调用重写方法.因此,invokespecial
现在也可以参考interface
方法.
坚持调用指令的类型和常量池项的类型,像旧规则之间的匹配,将意味着,在案件default
的方法,我们有时会需要两个池条目以描述同一目标的方法,一个用于invokeinterface
和一个不同的invokespecial
.