Следующий код компилирует отлично с Eclipse, но сбоями для компиляции с javac:
public class HowBizarre {
public static <P extends Number, T extends P> void doIt(P value) {
}
public static void main(String[] args) {
doIt(null);
}
}
Я упростил код, таким образом, T не используется вообще теперь. Однако, я не вижу оснований для ошибки. По некоторым причинам javac решает, что T обозначает Объект и затем жалуется, что Объект не соответствует границам T (который верен):
HowBizarre.java:6: несовместимые типы; выведенный аргумент (аргументы) типа java.lang. Число, java.lang. Объект не соответствует границам переменной (переменных) типа P, T
найденный:
<P,T>
пустотребуемый: пусто
doIt(null); ^
Обратите внимание, что, если я заменяю пустой параметр ненулевым значением, он компилирует прекрасный.
Какой из компиляторов ведет себя правильно и почему? Действительно ли это - ошибка одного из них?
Проблема связана со спецификацией JLS, которая требует, чтобы аргументы типа, которые в противном случае не могли быть отклонены, выводились как Объект
, даже если он не удовлетворяет ограничениям (и, следовательно, вызвать ошибку компиляции).
Ниже приводится отрывок из отчета об «ошибке» (который был дополнительно аннотирован для ясности):
«Ошибка» ID 6299211 - переменная типа метода: вывод прерван для null
Эта программа не компилируется:
public class Try { void m () { java.util.Collections.max (ноль); } }
Состояние : ЗАКРЫТО, НЕ ДЕФЕКТ.
Оценка : ЭТО НЕ ОШИБКА . Алгоритм вывода не может собрать какую-либо информацию из аргумента (
null
), и метод не вызывается там, где есть какие-либо ожидания в отношении возвращаемого значения. В таких случаях компилятор должен вывестиjava.lang.Object
для переменной типа.
JLS 15.12.2.8 Вывод неразрешенных аргументов типа
Все оставшиеся переменные типа, которые еще не были выведены, затем считаются имеющими тип
Объект
Однако
Объект
не является подтипомComparable < ? super Object>
и, следовательно, не входит в границы переменной типа в объявленииCollections.max
:
Использование явных параметров типа «устраняет» проблему:
HowBizarre.<Number,Integer>doIt(null); // compiles fine in javac
Чтобы показать, что это не имеет отношения к null
аргумент и многое другое, связанное с абсолютным отсутствием информации для определения типа, вы можете попробовать, например, любое из следующих объявлений:
<T,U extends Comparable<T>> void doIt()
<T extends Number,U extends T> void doIt()
В любом случае вызов doIt ();
не компилируется в javac
, поскольку он должен выводить U
для быть Объект
согласно 15.12.2.8, даже если это вызовет ошибку компиляции.
Хотя ни один из приведенных выше фрагментов не компилируется в некоторой версии javac
, все они компилируются в некоторой версии Eclipse. Это может указывать на ошибку со стороны Eclipse. Известно, что между разными компиляторами есть разногласия.
Это скорее ошибка в javac. Eclipse выводит правильный тип.
Вы можете обойти это, вызвав doIt ((Number) null);
Даже если вы не планируете использовать javac для разработки, исправьте эту проблему, потому что такие инструменты, как ant или maven, используют его и это вызовет проблемы, если вы когда-нибудь их представите.
из исследования полигенных смазочных материалов, солнечный javac явно соответствует спецификации. в прошлом я также использовал другие компиляторы, и когда возникал конфликт, всегда оказывалось, что солнечный javac правильный. Sun имеет то преимущество, что документирует свой опыт от внедрения в спецификацию, в то время как другим ребятам приходится читать спецификацию с нуля - действительно сложно не заснуть, когда вы ее читаете.