【问题标题】:Why does a generic cast of a List<? extends Set..> to List<Set..> succeed on Sun JDK 6 but fail to compile on Oracle JDK 7?为什么 List<?将 Set..> 扩展为 List<Set..> 在 Sun JDK 6 上成功但在 Oracle JDK 7 上编译失败?
【发布时间】:2017-06-23 23:22:57
【问题描述】:

以下代码

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

请注意,useListuseListOfSpecificSets 中的前 两个 "extends-to-specific-type" 强制转换在 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

【问题讨论】:

  • 不管怎样,这段代码在我的 JDK 7 上编译。你能把java -version 命令的整个输出连同“build”字符串一起发布吗?
  • 请粘贴确切的编译错误 + sanjay 的建议。 我相信在某些情况下,泛型在 java 7 中应该更简单...也许你发现了一个极端情况,旧的、复杂的 JDK 6 语法破坏了新的编译器。
  • @Sanjay:啊哈,有趣!请参阅编辑后的问题以获取对您的 cmets 的回复
  • javac 似乎有一个坏习惯,即自发拒绝根据 JLS 合法的泛型转换,请参阅bug 6790039,尤其是 looooong 列表 Related Bugs i> 那里提到过(目前有 16 个)- 你的可能在这个列表中 :)

标签: java generics java-7


【解决方案1】:

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

其中一条规则允许缩小引用转换...后跟未经检查的转换

此规则允许投射List&lt;A&gt; =&gt; List&lt;B&gt;

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

不过,这还不是全部;规范有进一步的规则来禁止像List&lt;String&gt;=&gt;List&lt;Integer&gt; 这样的转换,因为它们是可证明是不同的参数化的 类型。没有对象同时属于这两种类型,因此编译器认为最好禁止这种明显的编程错误。 (你可以通过明确的List&lt;String&gt;=&gt;List=&gt;List&lt;Integer&gt;绕过它)

最后一条规则在这里不适用;所以它看起来像一个 javac7 错误。

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

问题是List&lt;T&gt;List&lt;A&gt; 是否是可证明不同的参数化类型。我的理解是它是错误的(编译前两个示例必须是错误的)。由于T 是一个类型变量,它可以取一个值使List&lt;T&gt;List&lt;A&gt; 成为相同的参数化类型(即当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

【讨论】:

  • 你可以通过明确的 List=>List=>List) 来绕过它 我确实证实了这一点 - 但“黑客泛型”不是我真的很想提交 ;-) 感谢您的解释!您是否建议联系 Oracle 内部的任何人(例如编译器开发列表等?),看看是否值得为此提交错误?
  • 刚刚发布到编译器开发。很想知道我们会得到什么样的回应;-)
  • @AndrewPhillips 嘿,你能告诉我们这个回复吗 :) 我很想看到这个免费的黑客解决方案。
猜你喜欢
  • 1970-01-01
  • 2016-09-28
  • 1970-01-01
  • 2022-01-05
  • 2013-01-23
  • 2019-07-10
  • 1970-01-01
  • 1970-01-01
  • 2017-10-19
相关资源
最近更新 更多