invokestatic on interface中的静态方法

反汇编一些Java 8代码我发现一些对接口中静态方法的invokestatic调用(特别是java.util.function.Function.identity() )在const pool中使用了InterfaceMethodRef; 这就是javap -s -c -vp向我展示的内容:

  15: invokestatic #66 // InterfaceMethod java/util/function/Function.identity:()Ljava/util/function/Function; 

根据JVM 8规范,这是不可能的,当我在带有Java 7 major version=51major version=51 )的classfile中使用此指令时,它已在此指令上抛出VerifyError。

然而,当我将主要版本更改为52 ,它开始像魅力一样工作。 请注意,我在Oracle JDK 1.8.0_60上运行。 我想知道为什么需要这个更改(调用的方法是静态链接的,不是吗?)以及是否记录在任何地方。

好吧,在Java 8之前, interface s中的static方法是不允许的,所以很明显,无论在Java 8中如何实现,在任何尝试在先前版本或具有旧版本的类文件中使用它们都注定要失败。 。

在Java 8之前,我们有以下两个规则:

  1. CONSTANT_Methodref_info结构的class_index项必须是类类型,而不是接口类型。

    CONSTANT_InterfaceMethodref_info结构的class_index项必须是接口类型。

    (见JVMSpec7§4.4.2 )

  2. invokestatic的方法描述符必须引用CONSTANT_Methodref_info条目

    (见JVMSpec7§6.5 )

    该索引处的运行时常量池项必须是对方法(第5.1节)的符号引用,该方法给出方法的名称和描述符(第4.3.3节)以及对其中的类的符号引用。方法是找到的。

    可能看起来并不清楚“对方法的符号引用”会排除接口方法,但如果没有这种假设,我们根本不会对Java 8的行为产生任何影响。 通过与Java 8的JVM规范进行比较或考虑到接口方法总是暗示为非static它也将变得更加清晰。

显而易见的是,为了在interface s中添加对static方法的支持,要通过invokestatic调用,至少需要更改一个规则。

如果我们看一下规则及其措辞,我们会看到第一个很清楚,而在第二个中,“对方法的符号引用”指的是CONSTANT_Methodref_info条目并排除“对符号的引用”并不完全明显。界面方法“。 决定改变该规则并同时使措辞更清晰:

( JVMSpec8§6.5 ):

该索引处的运行时常量池项必须是对方法或接口方法(第5.1节)的符号引用,它提供方法的名称和描述符(第4.3.3节)以及对方法的符号引用。要在其中找到方法的类或接口。

现在很明显, invokestatic可能会引用接口方法,因此不需要触及第一条规则,也没有触及它 。 但请注意,第一条规则从未强制接口方法是非static 。 它只是关于声明类型是否是interface


这显然降低了CONSTANT_Methodref_infoCONSTANT_InterfaceMethodref_info之间区别的价值,但这是不可避免的。 如果放宽第一条规则,它也会软化这种区别。

但是有一个强有力的理由改变调用方面:由于引入了default方法,现在有可能通过invokespecial调用重写的default方法,就像之前的其他重写非abstract方法一样。 因此, invokespecial现在也可以引用interface方法。

坚持调用指令的类型和常量池条目类型之间的匹配,就像旧规则一样,意味着在default方法的情况default ,我们有时需要两个池条目来描述相同的目标方法,一个用于invokeinterfaceinvokespecial