Почему общий состав спискак спискупреуспеть в Sun JDK 6, но не скомпилировать в Oracle JDK 7? - PullRequest
13 голосов
/ 26 декабря 2011

Следующий код

class GenericCompilationFailureDemo {
    List<? extends GenericCompilationFailureDemo> newList() { 
        return new ArrayList<GenericCompilationFailureDemo>(); 
    };

    void useList() {
        List<GenericCompilationFailureDemo> list = 
            (List<GenericCompilationFailureDemo>) newList();
    }  

    List<? extends Set<GenericCompilationFailureDemo>> newListOfSpecificSets() { 
        return new ArrayList<Set<GenericCompilationFailureDemo>>(); 
    };

    void useListOfSpecificSets() {
        List<Set<GenericCompilationFailureDemo>> listOfSpecificSets = 
            (List<Set<GenericCompilationFailureDemo>>) newListOfSpecificSets();
    } 

    List<? extends Set<? extends GenericCompilationFailureDemo>> newListOfSets() { 
        return new ArrayList<Set<? extends GenericCompilationFailureDemo>>(); 
    };

    void useListOfSet() {
        List<Set<? extends GenericCompilationFailureDemo>> listOfSets = 
            (List<Set<? extends GenericCompilationFailureDemo>>) newListOfSets();
    }  
}

компилируется под Sun JDK 1.6.0_20 (64-битная в Windows Vista, но я не думаю, что это имеет какое-либо значение), но вызывает следующую ошибку компиляции под Oracle JDK1.7.0_01 (на той же платформе):

[ERROR] src\main\java\GenericCompilationFailureDemo.java:[56,78] error: inconvertible types

Обратите внимание, что первые два"приведения к конкретному типу" в useList и useListOfSpecificSets оба успешно завершаются при1.7.0_01, так что может показаться, что это как-то связано с «двойным обобщенным расширением».

Есть идеи, что могло измениться между 6 и 7, и соответствует ли наблюдаемое поведение спецификации или ошибке?

отредактировано в ответ на комментарий Санджая:

@ Санджай: Ага, интересно!Здесь вывод из java -version:

java version "1.7.0_01"
Java(TM) SE Runtime Environment (build 1.7.0_01-b08)
Java HotSpot(TM) 64-Bit Server VM (build 21.1-b02, mixed mode)

А вот результат javac GenericCompilationFailureDemo.java (тот же код, что и выше с инструкциями импорта для List, ArrayList и Set):

GenericCompilationFailureDemo.java:30: error: inconvertible types
            (List<Set<? extends GenericCompilationFailureDemo>>) newListOfSets()
;
                                                                              ^
  required: List<Set<? extends GenericCompilationFailureDemo>>
  found:    List<CAP#1>
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Set<? extends GenericCompilationFailureDemo> from capture of ?
 extends Set<? extends GenericCompilationFailureDemo>
Note: GenericCompilationFailureDemo.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
1 error

1 Ответ

7 голосов
/ 27 декабря 2011

Это явно ошибка javac7. Это должно быть разрешено согласно правилам конвертации при касте. [1]

Одно из правил допускает сужение эталонного преобразования ... с последующим неконтролируемым преобразованием

Кастинг List<A> => List<B> разрешен этим правилом

List<A> => List   // narrowing reference conversion
List => List<B>   // unchecked conversion

Хотя это еще не все; спецификация имеет дополнительные правила, запрещающие приведение типа List<String>=>List<Integer>, поскольку они доказуемо различны, параметризованы Типы . Не существует объектов, принадлежащих к двум типам одновременно, поэтому компилятор считает, что лучше не допускать этой очевидной ошибки программирования. (Вы можете обойти это явно List<String>=>List=>List<Integer>)

Последнее правило здесь не применимо; так что это похоже на ошибку javac7.

Почему последнее правило не применяется: поэтому мы приводим List<? extends A> к List<A>. Здесь преобразование захвата применяется к List<? extends A> [2], поэтому мы фактически приводим List<T> к List<A>, где T - это переменная нового типа с верхней границей A.

Вопрос в том, являются ли List<T> и List<A> доказуемо различными параметризованными типами . Я понимаю, что это неверно (это должно быть ложно для ваших первых двух примеров для компиляции). Поскольку T является переменной типа, она может принимать значение, чтобы List<T> и List<A> имели один и тот же параметризованный тип (т.е. когда T=A). Это рассуждение должно работать для любого типа A.

[1] http://java.sun.com/docs/books/jls/third_edition/html/conversions.html#5.5

[2] http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#341306

...