Я столкнулся со следующим странным случаем неполноты спецификации Java/JVM. Предположим, что у нас есть классы (мы будем использовать Java 1.8 и HotSpot):Бинарная совместимость изменения класса со статическими методами для интерфейса в Java
public class Class {
public static void foo() {
System.out.println("hi");
}
}
public class User {
public static void main(String[] args) {
Class.foo();
}
}
Затем перекомпилировать класс быть интерфейс без перекомпиляции Пользователь:
public interface Class {
public static void foo() {
System.out.println("hi");
}
}
Запуск User.main() теперь производит тот же вывод 'hi'. Это кажется очевидным, но я бы ожидать, что она не в состоянии с IncompatibleClassChangeError и вот почему:
Я знаю, что изменение класса с интерфейсом является бинарное Несовместимость согласно JVM 5.3.5#3 statement:
Если класс или интерфейс названный в качестве прямого суперкласса C, является, по сути, интерфейсом, загрузка вызывает IncompatibleClassChangeError.
Но предположим, что у нас нет наследников класса. Теперь нам нужно отнести спецификацию JVM о разрешении метода. Первая версия компилируется в этом байткод:
public static void main(java.lang.String[]);
Code:
0: invokestatic #2 // Method examples/Class.foo:()V
3: return
Таким образом, мы имеем здесь что-то под названием «CONSTANT_Methodref_info» в classpool.
Процитируем действия invokestatic.
... Во время выполнения элемента постоянного пула по этому индексу должен быть символической ссылкой на методе или метод интерфейса (§5.1), который дает имя и дескриптор (§ 4.3. 3) метода, а также символическую ссылку на класс или интерфейс, в котором этот метод должен быть найден. Именованный метод разрешен (§5.4.3.3).
Таким образом, JVM обрабатывает метод и методы интерфейса другим способом. В нашем случае JVM видит метод как метод класса (а не интерфейса). JVM пытается решить его соответственно 5.4.3.3 Метод Разрешение:
Согласно спецификации JVM, виртуальной машина должна провалиться по следующему высказыванию:
1) Если C является интерфейсом, разрешение метод выдает IncompatibleClassChangeError ,
потому что класс на самом деле не класс, а интерфейс.
К сожалению, я не нашел упоминаний о бинарной совместимости изменения класса для интерфейса в Спецификации языка Java Глава 13. Двоичная совместимость. Более того, о таком сложном случае упоминания одного и того же статического метода ничего не сказано.
Не могли бы кто-нибудь подробно остановиться на этом и показать мне, если я что-то пропустил?
Кстати, я нашел [упоминание] (https://wiki.eclipse.org/Evolving_Java-based_APIs_2#Evolving_API_packages): «Генетические изменения интерфейса API-интерфейса прерывают двоичную совместимость, даже в тех случаях, когда класс/интерфейс используется клиентом, но не реализован. Это связано с тем, что байт-коды Java VM для вызова метода, объявленного в интерфейсе, отличаются от тех, которые используются для вызова метода, объявленного в классе'. Но это нечто неофициальное. –