为什么列表的通用演员<?扩展Set ..>到List <Set ..>在Sun JDK 6上成功但在Oracle JDK 7上无法编译?

And*_*ips 13 java generics java-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();
    }  
}
Run Code Online (Sandbox Code Playgroud)

在Sun JDK 1.6.0_20下编译(在Windows Vista上为64位,但我认为没有任何区别)但在Oracle JDK 1.7.0_01(同一平台)下导致以下编译失败:

[ERROR] src\main\java\GenericCompilationFailureDemo.java:[56,78] error: inconvertible types
Run Code Online (Sandbox Code Playgroud)

请注意,前两个 "扩展到特定类型"转换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)
Run Code Online (Sandbox Code Playgroud)

这里的结果javac GenericCompilationFailureDemo.java(与上面相同的代码,包含List,ArrayList和Set的import语句):

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
Run Code Online (Sandbox Code Playgroud)

irr*_*ble 7

这显然是一个javac7错误.应该允许每个铸造转换规则[1]

其中一条规则允许缩小参考转换...然后是未经检查的转换

List<A> => List<B>此规则允许投射

List<A> => List   // narrowing reference conversion
List => List<B>   // unchecked conversion
Run Code Online (Sandbox Code Playgroud)

但这不是故事的全部; 规范有进一步的规则禁止铸造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