gpt4 book ai didi

java - 使用静态方法更改类以在 Java 中进行接口(interface)的二进制兼容性

转载 作者:搜寻专家 更新时间:2023-10-31 20:09:09 25 4
gpt4 key购买 nike

我遇到过以下 Java/JVM 规范不完整的奇怪案例。假设我们有类(我们将使用 Java 1.8 和 HotSpot):

public class Class {
public static void foo() {
System.out.println("hi");
}
}

public class User {
public static void main(String[] args) {
Class.foo();
}
}

然后将 Class 重新编译为接口(interface),而无需重新编译 User`:

public interface Class {
public static void foo() {
System.out.println("hi");
}
}

运行 User.main 现在会产生相同的输出 hi。这看起来很明显,但我预计它会因 IncompatibleClassChangeError 而失败,这就是原因:

根据 JVM 5.3.5#3 statement,我知道将类更改为接口(interface)是二进制不兼容问题:

If the class or interface named as the direct superclass of C is, in fact, an interface, loading throws an IncompatibleClassChangeError.

但假设我们没有 Class 的继承者。我们现在必须引用 JVM 规范中有关方法解析的内容。第一个版本被编译成这个字节码:

public static void main(java.lang.String[]);
Code:
0: invokestatic #2 // Method examples/Class.foo:()V
3: return

所以我们在类池中有一个叫做 CONSTANT_Methodref_info 的东西。

让我们引用 invokestatic 的 Action .

... 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. The named method is resolved (§5.4.3.3).

因此 JVM 以不同的方式对待方法和接口(interface)方法。在我们的例子中,JVM 将方法视为类(而非接口(interface))的方法。JVM 尝试相应地解决它 5.4.3.3 方法解决:

根据 JVM 规范,JVM 必须在以下语句上失败:

1) If C is an interface, method resolution throws an IncompatibleClassChangeError.

...因为 Class 实际上不是一个类,而是一个接口(interface)。

不幸的是,我没有在 Java 语言规范第 13 章二进制兼容性中找到任何关于将类更改为接口(interface)的二进制兼容性的提及。此外,对于引用同一个静态方法这种棘手的情况也没有提及。

如果我遗漏了什么,有人可以详细说明并告诉我吗?

最佳答案

首先,由于您的示例不包含继承,我们不必“假设我们没有该类的继承者”,根本就没有。因此,§5.3.5 的引用部分与此示例无关。

§6.5 中引用的部分,命名为“对方法或接口(interface)方法的符号引用”具有讽刺意味的是 change made an Java 8 to relax the restrictions .如果接口(interface)方法是 static,则明确允许调用 invokestatic 指令。

§5.4.3.3的第一个项目符号,你在最后提到的,声明方法解析应该无条件失败,如果声明类型是 interface,确实违反了,但无论如何它没有意义。因为它是无条件引用的,即 invokestatic 的文档没有说明接口(interface)方法应该使用不同的查找算法,这意味着调用 static 方法interface 通常是不可能的。

这显然不是规范的意图,它在 interface 中包含了 static 方法的显式添加功能,当然也应该是可调用的。

在您的示例中,调用类确实违反了规范,即 §4.4.2 ,因为它在声明类更改后通过 CONSTANT_Methodref_info 而不是 CONSTANT_InterfaceMethodref_info 引用接口(interface)方法。但是 invokestatic 指令文档的当前状态并未强制要求根据常量池项的类型更改行为(这可能是实际意图,但并非如此)。而且,如前所述,坚持当前的措辞意味着拒绝 interface 上的任何 invokestatic

因此规范需要再次清理,但是由于 Methodref_infoInterfaceMethodref_info 之间的区别目前还没有 Java 8 之前那么有用(与上面链接的答案),如果最终的解决办法是在未来完全消除这种区别,我不会感到惊讶。

关于java - 使用静态方法更改类以在 Java 中进行接口(interface)的二进制兼容性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42294217/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com