JLS 的哪一部分指定您不能从 List< 进行转换?将 List<Superclass>> 扩展到 List<List<Subclass>>?

Swe*_*per 11 java language-lawyer

(这个问题的灵感来自这个问题,我错误地回答了这个问题。)

此代码无法编译:

List<? extends List<Number>> list = new ArrayList<>();
List<List<Double>> anotherList = (List<List<Double>>) list;
Run Code Online (Sandbox Code Playgroud)

请注意,IntelliJ 不会报告任何错误。当我单击“运行”时,它只会编译失败。

我理解为什么这不能在概念层面上编译。list是“扩展的List<Number>东西”的列表,而“东西”永远不会是List<Double>,因为List<Double>它不是 的子类型List<Number>,并且因为它们具有相同的擦除,所以没有类型可以实现两者。

但是,当我尝试按照语言规范中的措辞来确定此强制转换是否有效时,我发现语言规范似乎说这是一个有效的强制转换!

这是我的推理:

强制转换满足从S( List<? extends List<Number>>) 到T( List<List<Double>>)的窄引用转换的所有三个要求。

5.1.6.1 . 允许的缩小参考转换

如果以下所有条件都为真,则存在从引用类型 S 到引用类型 T 的收缩引用转换:

  • S 不是 T 的亚型

  • 如果存在作为 T 超类型的参数化类型 X 和作为 S 超类型的参数化类型 Y,使得 X 和 Y 的擦除相同,则 X 和 Y 不可证明不同(第 4.5 节) .

  • 以下情况之一适用:

    • S 和 T 是接口类型。
    • [...]

第一点和第三点是微不足道的。为了证明第二点是正确的,我们取Collection<List<Double>>为 的参数化超类型List<List<Double>>,以及Collection<? extends List<Number>>的参数化超类型List<? extends List<Number>>。它们都擦除为相同的类型Collection。现在我们需要证明Collection<? extends List<Number>>Collection<List<Double>>不可证明是不同的(第 4.5 节)。同样的论点也适用于Iterable<...>.

编辑:我刚刚意识到 的超类型List<List<Double>>还包括诸如 的东西List<? extends List<Double>>,而不仅仅是List. 但我不认为这会否定这一说法,因为点是1出来X,并Y至少有一个通配符2的通配符范围/类型参数XY彼此的亚型。

如果以下任一条件为真,则两个参数化类型可证明是不同的:

  • 它们是不同泛型类型声明的参数化。

  • 它们的任何类型参数都可以证明是不同的。

显然,由于它们都擦除为相同的类型,因此第一个条件不可能为真。我们只需要证明第二个条件为假。

§4.5.1 中,规范定义了“类型参数可以证明是不同的”:

如果以下条件之一为真,则两个类型参数可证明是不同的:

  • [...]
  • 一种类型参数是类型变量或通配符,其上限(来自捕获转换(第 5.1.10 节),如有必要)为 S;另一个类型参数 T 不是类型变量或通配符;也没有|S| <: |T| 也不|T| <: |S| (第 4.8 节、第 4.10 节)。

(为简洁起见,没有显示其他(微不足道的错误)条件)这里,SisList<Number>Tis List<Double>。两者 |S| <: |T| 和|T| <: |S| 成立,如 |S| 和|T| 是相同的类型。是的,子类型关系是自反的,因为超类型关系是自反的(它被定义为直接超类型关系上的自反和传递闭包)。

因此类型参数Collection<? extends List<Number>>Collection<List<Double>>不可证明不同,因此List<? extends List<Number>>List<List<Double>>没有可证明不同,所以有(或应是)转换从List<? extends List<Number>>List<List<Double>>

我的推理错误在哪里?我错过了规范的其他部分吗?

Eug*_*ene 2

好吧,我会在Holger 确认并回答后说:JLS 在这个位置(至少)未指定。有一些相关的 JDK bug 也围绕着同样的想法,值得注意的是这个,它通过以下方式直接解决了你的问题:

\n
\n

....否则,将通配符和类型变量映射到它们的上限,然后测试它们的擦除是否是相关的类或接口(即,一个擦除的类型是另一种的子类型)

\n
\n

只是为了立即开始下一句话:

\n
\n

这是不健全的...

\n
\n

因此,该错误承认 JLS 需要围绕本章进行一些更正。

\n

从您对 JLS 的引用中,我也一直在努力解决两点:

\n
    \n
  • 一个类型参数是类型变量或通配符,具有上限(如有必要,来自捕获转换 (\xc2\xa75.1.10) ...

    \n

    我确实知道什么是捕获转换,但我不知道它可能是可选执行的(通过“如果需要”)。我一直认为它在每个地点、每时每刻都在进行。

    \n
  • \n
  • 捕获的转换类型上限是多少?

    \n

    例如,就您而言,是上限 List<Number>还是?List<?>根据我的理解(或者缺乏精确的 JLS 解释),这可以用任何一种方式来理解。

    \n
  • \n
\n

所有这些(+你对 JLS 的巨大刮擦)让我怀疑这里 JLS 的正确性,特别是因为它javac不遵循这些完全相同的规则。

\n