Компиляторы ведут себя по-разному с нулевым параметром общего метода

Следующий код отлично компилируется с 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 обозначает Object, а затем жалуется, что Object не соответствует границам T (что истинно):

HowBizarre.java:6: несовместимые типы; аргумент (-ы) вывода java.lang.Number, java.lang.Object не соответствуют ограничениям типа переменная P, T

найдено: <P,T> void

требуется: void

       doIt(null);
           ^

Обратите внимание, что если я заменил нулевой параметр на ненулевое значение, он компилируется отлично.

Какой из компиляторов ведет себя правильно и почему? Это ошибка одного из них?

Ответ 1

Проблема связана с спецификацией JLS, которая предусматривает, что в противном случае неопределяемые аргументы типа должны быть определены как Object, даже если они не удовлетворяют границам (и, следовательно, запускают ошибку компиляции).

Ниже приводится отрывок из отчета "ошибка" (который был дополнительно аннотирован для ясности):

"Bug" ID 6299211 - тип типа метода: логический вывод для null

Эта программа не компилируется:

public class Try {
    void m() {
        java.util.Collections.max(null);
    }
}

Состояние: ЗАКРЫТО, НЕ ДЕФЕКТ.

Оценка: ЭТО НЕ БЫЛО. Алгоритм вывода не может собрать какую-либо информацию из аргумента (null), и метод не вызывается в месте, где ожидаются ожидаемые значения. В таких случаях компилятор должен вывести java.lang.Object для переменной типа.


JLS 15.12.2.8 Вывод аргументов неразрешенного типа

Любые оставшиеся переменные типа, которые еще не были выведены, затем выводятся с типом Object


Однако Object не является подтипом Comparable<? super Object> и, следовательно, не входит в пределы переменной типа в объявлении Collections.max:

<T extends Object & Comparable<? super T> > T max(Collection<? extends T>)


Дальнейшие исследования

Использование явных параметров типа "исправляет" проблему:

HowBizarre.<Number,Integer>doIt(null); // compiles fine in javac

Чтобы показать, что это имеет меньшее отношение к аргументу null и больше относится к абсолютному отсутствию информации о типе inferrence, вы можете попробовать, например. либо из следующих объявлений:

<T,U extends Comparable<T>> void doIt()

<T extends Number,U extends T> void doIt()

В любом случае вызов doIt(); не компилируется в javac, так как он должен вывести U как Object в соответствии с 15.12.2.8, даже если это приведет к ошибке компиляции.


Примечание по Eclipse

Хотя ни один из фрагментов выше не компилируется в какой-либо версии javac, они все делают в некоторой версии Eclipse. Это может означать ошибку в части Eclipse. Было известно, что между различными компиляторами существуют разногласия.

Связанные вопросы

Ответ 2

Это скорее ошибка в javac. Eclipse указывает правильный тип.

Вы можете использовать его, позвонив doIt((Number) null);

Даже если вы не планируете использовать javac для разработки, исправьте эту проблему, потому что такие инструменты, как ant или maven, используют ее, и это вызовет проблемы, если вы их представите в какой-то момент.

Ответ 3

из исследования полигенных смазочных материалов, солнце javac, по-видимому, соответствует спецификации. в прошлом я также использовал другие компиляторы, и когда возник конфликт, всегда получилось, что sun javac верен. Преимущество Sun заключается в том, чтобы документировать свой опыт от внедрения в спецификацию, в то время как другие ребята должны прочитать спецификацию с нуля - действительно трудно не заснуть, когда вы ее читаете.