Я столкнулся с интересным фрагментом кода Java, который указывает IntelliJ как ошибку, но который javac
принимает как законный. Либо IntelliJ ошибается, и код является законным, либо компилятор является "неправильным", будь то из-за ошибки или преднамеренного ослабления правил.
Мне нравится думать, что я хорошо понимаю систему типов Java, и мои собственные рассуждения заставляют меня подозревать, что IntelliJ ошибается и javac
прав. Тем не менее, у меня есть чертовски время, грохочущее JLS, и я хотел бы знать наверняка.
Прежде чем попасть в проблемный код, давайте посмотрим на какой-то похожий код определенно незаконный:
interface A<T> {}
interface X extends A<String> {}
interface Y extends A<Object> {}
interface Z extends X, Y {} // COMPILE ERROR
Как и следовало ожидать, как IntelliJ, так и javac
правильно отмечают это как ошибку: "A" не может быть унаследован разными аргументами типа: "java.lang.String" и "java". lang.Object".
Нет проблем. Но что делать, если мы делаем X
и Y
generic, причем Z
расширяем свою необработанную форму?
interface X<T> extends A<String> {}
interface Y<T> extends A<Object> {}
interface Z extends X, Y {} // OK according to javac, ERROR according to IntelliJ
Здесь IntelliJ с готовностью сообщает ту же ошибку, что и для первого фрагмента, но javac
с радостью принимает код как написано.
Я понимаю, что исходные типы рекурсивно стираются, что означает, что все суперклассы и суперинтерфейсы исходного типа заменяются на их рекурсивно стираемые формы и т.д. Таким образом, в проблемном коде Z
заканчивается расширением (raw) A
через X
и Y
, в отличие от первого примера, в котором Z
продолжается A<String>
через X
и A<Object>
через Y
.
Если это действительно так, я бы сделал вывод, что IntelliJ ошибочен, а javac
верен: второй фрагмент кода является законным.
Что скажешь, эксперты Stack Overflow?