在接口中的静态方法上调用静态
invokestatic on static method in interface
反汇编一些 Java 8 代码 我发现一些 invokestatic
调用接口中的静态方法(特别是 java.util.function.Function.identity()
)使用常量池中的 InterfaceMethodRef;这就是 javap -s -c -v p
给我看的:
15: invokestatic #66 // InterfaceMethod java/util/function/Function.identity:()Ljava/util/function/Function;
根据 JVM 8 spec 这是不可能的,当我在版本为 Java 7 (major version=51
) 的类文件中使用这条指令时,它在这条指令上抛出了 VerifyError .
然而,当我将主要版本更改为 52
时,它开始像一个魅力一样工作。请注意,我在 Oracle JDK 1.8.0_60 上 运行。我想知道为什么需要进行此更改(调用的方法是静态链接的,不是吗?)以及是否在任何地方对此进行了记录。
好吧,在 Java 8 之前,interface
中的 static
方法是不允许的,所以很明显,任何试图在以前的版本或 [=86] 中使用它们的尝试=] 具有旧版本的文件注定会失败,无论它在 Java 8.
中是如何实现的
在 Java 8 之前,我们有以下两个规则:
The class_index item of a CONSTANT_Methodref_info
structure must be a class type, not an interface type.
The class_index item of a CONSTANT_InterfaceMethodref_info
structure must be an interface type.
(参见JVMSpec 7 §4.4.2)
invokestatic
的方法描述符必须引用 CONSTANT_Methodref_info
条目
(参见JVMSpec 7 §6.5)
The run-time constant pool item at that index must be a symbolic reference to a method (§5.1), which gives the name and descriptor (§4.3.3) of the method as well as a symbolic reference to the class in which the method is to be found.
“对方法的符号引用”排除接口方法可能看起来不太清楚,但如果没有这个假设,我们对 Java 8 的行为没有任何影响。通过与 Java 8 的 JVM 规范进行比较,或者考虑到接口方法总是暗示为非 static
.
,它也会变得更加清晰
显然,要在 interface
中添加对 static
方法的支持,以便通过 invokestatic
调用,至少必须更改一条规则。
如果我们查看规则及其措辞,我们会发现第一个非常清楚,而在第二个中,“对方法的符号引用”指的是 CONSTANT_Methodref_info
并不完全明显条目并排除“对接口方法的符号引用”。决定是更改该规则并同时使措辞更清晰:
The run-time constant pool item at that index must be a symbolic reference to a method or an interface method (§5.1), which gives the name and descriptor (§4.3.3) of the method as well as a symbolic reference to the class or interface in which the method is to be found.
现在很明显,invokestatic
可能是指接口方法,因此第一条规则不需要动,it hasn’t been touched。但请注意,第一条规则从未强制要求接口方法是非 static
。只是关于声明类型是否为interface
。
这显然降低了 CONSTANT_Methodref_info
和 CONSTANT_InterfaceMethodref_info
之间的区别的价值,但这是不可避免的。如果第一条规则放宽,它也会软化这种区别。
但是有充分的理由改为更改调用方:由于引入了 default
方法,现在可以通过 [=28= 调用覆盖的 default
方法] 就像之前其他重写的非 abstract
方法一样。因此,invokespecial
现在也可以引用 interface
方法。
坚持调用指令的类型和常量池条目类型之间的匹配,就像旧规则一样,这意味着在 default
方法的情况下,我们有时需要两个池条目描述相同的目标方法,一个用于 invokeinterface
,另一个用于 invokespecial
.
反汇编一些 Java 8 代码 我发现一些 invokestatic
调用接口中的静态方法(特别是 java.util.function.Function.identity()
)使用常量池中的 InterfaceMethodRef;这就是 javap -s -c -v p
给我看的:
15: invokestatic #66 // InterfaceMethod java/util/function/Function.identity:()Ljava/util/function/Function;
根据 JVM 8 spec 这是不可能的,当我在版本为 Java 7 (major version=51
) 的类文件中使用这条指令时,它在这条指令上抛出了 VerifyError .
然而,当我将主要版本更改为 52
时,它开始像一个魅力一样工作。请注意,我在 Oracle JDK 1.8.0_60 上 运行。我想知道为什么需要进行此更改(调用的方法是静态链接的,不是吗?)以及是否在任何地方对此进行了记录。
好吧,在 Java 8 之前,interface
中的 static
方法是不允许的,所以很明显,任何试图在以前的版本或 [=86] 中使用它们的尝试=] 具有旧版本的文件注定会失败,无论它在 Java 8.
在 Java 8 之前,我们有以下两个规则:
The class_index item of a
CONSTANT_Methodref_info
structure must be a class type, not an interface type.The class_index item of a
CONSTANT_InterfaceMethodref_info
structure must be an interface type.(参见JVMSpec 7 §4.4.2)
invokestatic
的方法描述符必须引用CONSTANT_Methodref_info
条目(参见JVMSpec 7 §6.5)
The run-time constant pool item at that index must be a symbolic reference to a method (§5.1), which gives the name and descriptor (§4.3.3) of the method as well as a symbolic reference to the class in which the method is to be found.
“对方法的符号引用”排除接口方法可能看起来不太清楚,但如果没有这个假设,我们对 Java 8 的行为没有任何影响。通过与 Java 8 的 JVM 规范进行比较,或者考虑到接口方法总是暗示为非
static
. ,它也会变得更加清晰
显然,要在 interface
中添加对 static
方法的支持,以便通过 invokestatic
调用,至少必须更改一条规则。
如果我们查看规则及其措辞,我们会发现第一个非常清楚,而在第二个中,“对方法的符号引用”指的是 CONSTANT_Methodref_info
并不完全明显条目并排除“对接口方法的符号引用”。决定是更改该规则并同时使措辞更清晰:
The run-time constant pool item at that index must be a symbolic reference to a method or an interface method (§5.1), which gives the name and descriptor (§4.3.3) of the method as well as a symbolic reference to the class or interface in which the method is to be found.
现在很明显,invokestatic
可能是指接口方法,因此第一条规则不需要动,it hasn’t been touched。但请注意,第一条规则从未强制要求接口方法是非 static
。只是关于声明类型是否为interface
。
这显然降低了 CONSTANT_Methodref_info
和 CONSTANT_InterfaceMethodref_info
之间的区别的价值,但这是不可避免的。如果第一条规则放宽,它也会软化这种区别。
但是有充分的理由改为更改调用方:由于引入了 default
方法,现在可以通过 [=28= 调用覆盖的 default
方法] 就像之前其他重写的非 abstract
方法一样。因此,invokespecial
现在也可以引用 interface
方法。
坚持调用指令的类型和常量池条目类型之间的匹配,就像旧规则一样,这意味着在 default
方法的情况下,我们有时需要两个池条目描述相同的目标方法,一个用于 invokeinterface
,另一个用于 invokespecial
.