Подтвердить что ты не робот

Почему общий листинг списка <? extends Set..> to List <Set..> преуспеть на Sun JDK 6, но не скомпилировать на Oracle JDK 7?

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

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 JDK 1.7.0_01 (той же платформе):

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

Обратите внимание, что первые два типа "extends-to-specific-type" в useList и useListOfSpecificSets оба по-прежнему преуспевают в 1.7.0_01, поэтому, похоже, это что-то связано с "двойным родовым расширением".

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

отредактирован в ответ на комментарий Sanjay:

@Sanjay: Ага, интересно! Здесь вывод 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
4b9b3361

Ответ 1

Это, по-видимому, ошибка 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