简体   繁体   English

使用静态方法将类更改为Java中的接口的二进制兼容性

[英]Binary compatibility of changing a class with static methods to interface in Java

I've faced the following weird case of an incompleteness of the Java/JVM specification. 我遇到了以下奇怪的Java / JVM规范不完整的情况。 Suppose we have the classes (we will use Java 1.8 and HotSpot): 假设我们有类(我们将使用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();
  }
}

Then recompile the Class to be an interface without recompiling the User`: 然后重新编译Class to be an interface without recompiling the User`:

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

Running the User.main now produces the same output hi . 运行User.main现在产生相同的输出hi It seems obvious, but I would expect it to fail with the IncompatibleClassChangeError and that's why: 这似乎是显而易见的,但我希望它会因IncompatibleClassChangeError而失败,这就是原因:

I know that changing a class to an interface is a binary incompatibility according to JVM 5.3.5#3 statement : 我知道根据JVM 5.3.5#3语句将类更改为接口是二进制不兼容:

If the class or interface named as the direct superclass of C is, in fact, an interface, loading throws an IncompatibleClassChangeError . 如果命名为C的直接超类的类或接口实际上是一个接口,则加载会抛出IncompatibleClassChangeError

But let's assume we don't have inheritors of the Class . 但是我们假设我们没有Class继承者。 We now have to refer the JVM specification about method's resolution. 我们现在必须参考JVM规范来了解方法的分辨率。 The first version is compiled into this bytecode: 第一个版本被编译成这个字节码:

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

So we have here something called CONSTANT_Methodref_info in classpool. 所以我们在这里有一个叫做类池中的CONSTANT_Methodref_info的东西。

Let's quote the actions of the invokestatic . 让我们引用invokestatic的动作。

... 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. ...该索引处的运行时常量池项必须是对方法接口方法 (第5.1节)的符号引用,它提供方法的名称和描述符(第4.3.3节)以及符号引用要在其中找到方法的类或接口。 The named method is resolved (§5.4.3.3) . 解决指定的方法(第5.4.3.3节)

So the JVM treats method and interface methods in a different manner. 因此JVM以不同的方式处理方法和接口方法。 In our case, JVM sees the method to be a method of the class (not interface). 在我们的例子中,JVM将该方法视为类的方法(而不是接口)。 JVM tries to resolve it accordingly 5.4.3.3 Method Resolution: JVM尝试相应地解决它5.4.3.3方法解析:

According to JVM specification, the JVM must fail on the following statement: 根据JVM规范,JVM必须在以下语句中失败:

1) If C is an interface, method resolution throws an IncompatibleClassChangeError. 1)如果C是接口,则方法解析会抛出IncompatibleClassChangeError。

...because Class is not actually a class, but an interface. ...因为Class实际上不是一个类,而是一个接口。

Unfortunately, I haven't found any mentions about binary compatibility of changing a class to interface in the Java Language Specification Chapter 13. Binary Compatibility. 不幸的是,我没有找到任何关于在Java语言规范第13章中将类更改为接口的二进制兼容性的提及。二进制兼容性。 Moreover, there is nothing said about such tricky case of referencing the same static method. 此外,没有任何关于引用相同静态方法的棘手案例的说法。

Could anybody elaborate on that and show me if I missed something? 任何人都可以详细说明并告诉我,如果我错过了什么?

First, since your example doesn't contain inheritance, we don't have to “assume we don't have inheritors of the Class”, there simply are none. 首先,由于您的示例不包含继承,因此我们不必“假设我们没有类的继承者”,根本就没有。 So the cited part of §5.3.5 is irrelevant for this example. 因此,§5.3.5中引用的部分与此示例无关。

The cited part of §6.5, naming “ symbolic reference to a method or an interface method ” is ironically a change made an Java 8 to relax the restrictions . 引用的§6.5部分,命名为“ 对方法或接口方法的符号引用 ”具有讽刺意味的是,Java 8放宽了限制 The invokestatic instruction is explicitly allowed to be invoked on interface methods, if they are static . 如果接口方法是static ,则显式允许在接口方法上调用invokestatic指令。

The first bullet of §5.4.3.3 , which you refer to at the end, stating that the method resolution should fail unconditionally, if the declaring type is an interface , is indeed violated, but it makes no sense anyway. §5.4.3.3的第一个项目,你在最后提到,指出方法解决方案应该无条件地失败,如果声明类型是一个interface ,确实被违反了,但无论如何它都没有意义。 Since it's referred to unconditionally, ie the documentation of invokestatic doesn't state that a different lookup algorithm should be used for interface methods, it would imply that invoking a static method of an interface is impossible in general. 由于它是无条件引用的,即invokestatic的文档没有说明应该为接口方法使用不同的查找算法,这意味着通常不可能调用interfacestatic方法。

That's obviously not the intention of the specification, which incorporates the explicitly added feature of static methods in interface s, which of course shall also be callable. 这显然不是规范的意图,它在interface s中包含static方法的明确添加的特性,当然也应该是可调用的。

In your example, the calling class does indeed violate the specification, namely §4.4.2 , as it refers to an interface method via CONSTANT_Methodref_info instead of CONSTANT_InterfaceMethodref_info , after the declaring class has been changed. 在您的示例中,调用类确实违反了规范,即§4.4.2 ,因为它在声明类更改后通过CONSTANT_Methodref_info而不是CONSTANT_InterfaceMethodref_info引用接口方法。 But the current state of the invokestatic instruction's documentation doesn't mandate to change the behavior based on the constant pool item's type (that might be the actual intention, but it's not there). 但是, invokestatic指令文档的当前状态并没有invokestatic要求根据常量池项的类型更改行为(可能是实际意图,但它不存在)。 And, as said, adhering to current wording would imply rejecting any invokestatic on an interface . 并且, invokestatic ,坚持当前的措辞意味着拒绝interface上的任何invokestatic

So the specification needs another cleanup, but since the distinction between Methodref_info and InterfaceMethodref_info is by far not as useful as it might have been before Java 8 (compare with the answer linked above), I wouldn't be surprised, if the ultimate fix turns out to be removing the distinction altogether in the future. 因此规范需要另一次清理,但是由于Methodref_infoInterfaceMethodref_info之间的区别远远没有Java 8之前那么有用(与上面链接的答案相比),如果最终修复转向,我不会感到惊讶将来完全消除这种区别。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM