(这个问题的灵感来自这个问题 https://stackoverflow.com/q/66037939/5133585,我错误地回答了。)
此代码无法编译:
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>>
) to T
(List<List<Double>>
).
5.1.6.1 https://docs.oracle.com/javase/specs/jls/se14/html/jls-5.html#jls-5.1.6.1。允许缩小参考转换
存在从引用类型 S 到
如果满足以下所有条件,则引用类型 T:
第一点和第三点确实是正确的。为了证明第二点是正确的,我们取Collection<List<Double>>
成为参数化的超类型List<List<Double>>
, and Collection<? extends List<Number>>
成为参数化的超类型List<? extends List<Number>>
。他们都擦除为相同的类型Collection
。现在我们需要证明Collection<? extends List<Number>>
and Collection<List<Double>>
不是可证明不同(§4.5) https://docs.oracle.com/javase/specs/jls/se14/html/jls-4.html#jls-4.5。同样的论点也适用于Iterable<...>
.
Edit: I just realised that the supertypes of List<List<Double>>
also include things like List<? extends List<Double>>
, not just the superinterfaces of List
. But I don't think that will invalidate this argument, as the point is that 1. out of X
and Y
there is at least one wildcard 2. the wildcard bounds/type arguments of X
and Y
are subtypes of each other.
两个参数化类型可以证明是不同的,如果
以下是正确的:
-
它们是不同泛型类型声明的参数化。
-
它们的任何类型参数都可以证明是不同的。
显然,由于它们都擦除为相同类型,因此第一个条件不可能为真。我们只需要证明第二个条件是错误的。
In §4.5.1 https://docs.oracle.com/javase/specs/jls/se14/html/jls-4.html#jls-4.5.1,规范定义了“类型参数可证明是不同的”:
如果满足以下条件之一,则两个类型参数可证明是不同的
真的:
- [...]
- 一个类型参数是类型变量或通配符,上限为 S(来自捕获转换(第 5.1.10 节),如有必要);和
其他类型参数 T 不是类型变量或通配符;也没有
|S|
(为了简洁起见,没有显示其他(完全错误的)条件)这里,S
is List<Number>
and T
is List<Double>
。两者|S| direct超类型关系)。
因此,类型参数Collection<? extends List<Number>>
and Collection<List<Double>>
无法证明是不同的,所以List<? extends List<Number>>
and List<List<Double>>
无法证明不同,因此存在(或应该) 的转换List<? extends List<Number>>
to List<List<Double>>
!
我的推理错误在哪里?我错过了规范的其他部分吗?