Компиляторы ведут себя по-разному с нулевым параметром универсального метода - PullRequest
16 голосов
/ 08 июня 2010

Следующий код прекрасно компилируется с 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: несовместимые типы;выводимый тип аргумента (ов): void

       doIt(null);
           ^

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

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

Ответы [ 3 ]

18 голосов
/ 08 июня 2010

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

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

«Ошибка» ID 6299211 - переменная типа метода: логический вывод прерван для нуля

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

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 extendsObject & Comparable<? super T>> T max(Collection<? extends T>)


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

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

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, чтобы быть Object согласно 15.12.2.8, даже если это вызовет ошибку компиляции.


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

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

Похожие вопросы

4 голосов
/ 08 июня 2010

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

Вы можете обойти это, позвонив doIt((Number) null);

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

2 голосов
/ 09 июня 2010

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

...