I've faced the following weird case of an incompleteness of the Java/JVM specification. Suppose we have the classes (we will use Java 1.8 and 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`:
public interface Class {
public static void foo() {
System.out.println("hi");
}
}
Running the User.main
now produces the same output hi
. It seems obvious, but I would expect it to fail with the IncompatibleClassChangeError
and that's why:
I know that changing a class to an interface is a binary incompatibility according to JVM 5.3.5#3 statement:
If the class or interface named as the direct superclass of C is, in fact, an interface, loading throws an
IncompatibleClassChangeError
.
But let's assume we don't have inheritors of the Class
. We now have to refer the JVM specification about method's resolution.
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.
Let's quote the actions of the 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. The named method is resolved (§5.4.3.3).
So the JVM treats method and interface methods in a different manner. In our case, JVM sees the method to be a method of the class (not interface). JVM tries to resolve it accordingly 5.4.3.3 Method Resolution:
According to JVM specification, the JVM must fail on the following statement:
1) If C is an interface, method resolution throws an IncompatibleClassChangeError.
...because Class
is not actually a class, but an interface.
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. 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?