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

Which part of the JLS specifies that you can't cast from List<? extends List<Superclass>> to List<List<Subclass>>?

(此题灵感来自this question,我答错了。)

此代码无法编译:

List<? extends List<Number>> list = new ArrayList<>();
List<List<Double>> anotherList = (List<List<Double>>) list;

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

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

但是,当我试图按照语言规范中的措辞来判断这个转换是否有效时,我发现语言规范似乎说这是一个有效的转换!

这是我的推理:

演员表满足从 S (List<? extends List<Number>>) 到 T (List<List<Double>>) 的缩小参考转换的所有三个要求。

5.1.6.1. Allowed Narrowing Reference Conversion

A narrowing reference conversion exists from reference type S to reference type T if all of the following are true:

  • S is not a subtype of T

  • If there exists a parameterized type X that is a supertype of T, and a parameterized type Y that is a supertype of S, such that the erasures of X and Y are the same, then X and Y are not provably distinct (§4.5).

  • One of the following cases applies:

    • S and T are interface types.
    • [...]

第一点和第三点完全正确。为了证明第二点是正确的,我们将 Collection<List<Double>> 视为 List<List<Double>> 的参数化超类型,并将 Collection<? extends List<Number>> 视为 List<? extends List<Number>> 的参数化超类型。它们都擦除为相同类型 Collection。现在我们需要证明 Collection<? extends List<Number>>Collection<List<Double>> 不是 provably distinct (§4.5)。同样的论点也适用于 Iterable<...>.

编辑:我刚刚意识到 List<List<Double>> 的超类型还包括 List<? extends List<Double>> 之类的东西,而不仅仅是 List 的超接口。但我认为这不会使这个论点无效,因为关键是 1. XY 中至少有一个通配符 2. [ 的通配符 bounds/type 参数=33=] 和 Y 是彼此的子类型。

Two parameterized types are provably distinct if either of the following is true:

  • They are parameterizations of distinct generic type declarations.

  • Any of their type arguments are provably distinct.

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

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

Two type arguments are provably distinct if one of the following is true:

  • [...]
  • One type argument is a type variable or wildcard, with an upper bound (from capture conversion (§5.1.10), if necessary) of S; and the other type argument T is not a type variable or wildcard; and neither |S| <: |T| nor |T| <: |S| (§4.8, §4.10).

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

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

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

好吧,我会在 Holger's confirmation and answer: JLS is underspecified (at least) at this location. There are some related JDK bugs that work around the same idea, noticeable this one 之后再说,这直接解决了您的问题:

.... Otherwise, map wildcards and type variables to their upper bounds, and then test whether their erasures are related classes or interfaces (that is, one erased type is a subtype of the other)

只是立即开始下一句:

This is unsound...

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

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

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

    我确实知道 捕获转换 是什么,但我不知道它可能是可选的(通过“必要时”)执行。我一直以为它是在每个位置,一直在执行。

  • 什么是捕获的转化类型上限

    在你的情况下,是 上限 List<Number> 还是 List<?>?以我的理解(或缺乏精确的JLS解释),这两种方式都可以理解。

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