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. X
和 Y
中至少有一个通配符 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).
(为简洁起见,未显示其他(微不足道的错误)条件)这里,S
是 List<Number>
,T
是 List<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
没有遵循这些完全相同的规则。
(此题灵感来自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. X
和 Y
中至少有一个通配符 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).
(为简洁起见,未显示其他(微不足道的错误)条件)这里,S
是 List<Number>
,T
是 List<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
没有遵循这些完全相同的规则。