为什么 List<的通用转换?扩展 Set..>列表<设置。>在 Sun JDK 6 上成功,但在 Oracle JDK 7 上编译失败?

2022-09-04 22:46:33

下面的代码

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 下编译(在 Windows Vista 上为 64 位,但我认为这没有任何区别),但在 Oracle JDK 1.7.0_01(相同平台)下会导致以下编译失败:

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

请注意,前个“扩展到特定类型”强制转换,并且在1.7.0_01下仍然成功,因此它似乎与“双泛型扩展”有关。useListuseListOfSpecificSets

任何想法,在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)

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

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

这显然是一个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错误。

为什么最后一条规则不适用:所以我们要转换为 。这里捕获转换应用于 [2],因此我们实际上将转换为 ,其中是一个具有上限的新类型变量。List<? extends A>List<A>List<? extends A>List<T>List<A>TA

问题是 和 是否是可证明不同的参数化类型。我的理解是,它是假的(对于编译的前两个示例,它必须是假的)。由于 是 类型变量,因此可以采用一个值和相同的参数化类型(即 when )。此推理应适用于任何类型的 。List<T>List<A>TList<T>List<A>T=AA

[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


答案 2

推荐