// #1 (does compile)
List raw = null;
List<?> wild = raw;
// #2 (doesn't compile)
List<List> raw = null;
List<List<?>> wild = raw;
首先让我们理清为什么这些实际上是不相关的任务。也就是说,它们受不同规则的约束。
#1 称为未经检查的转换:
从原始类或接口类型(第 4.8 节)到表单的任何参数化类型都有未经检查的转换。G
G<T1,...,Tn>
具体来说,它是仅针对这种情况的分配上下文的特例:
如果在应用 [其他可能的转换] 之后,结果类型是原始类型,则可能会应用未经检查的转换。
#2 需要引用类型转换;然而,它的问题在于它不是一个扩大转换(这是一种在没有强制转换的情况下隐式允许的引用转换)。
这是为什么?好吧,这特别受通用子类型规则的约束,更具体地说是这个要点:
给定一个泛型类型声明( n > 0),参数化类型的直接超类型,其中(1 ≤ i ≤ n ) 是一个类型,如下:C<F1,...,Fn>
C<T1,...,Tn>
Ti
C<S1,...,Sn>
,其中包含(1 ≤ i ≤ n )。Si
Ti
这指的是 JLS 称为包含的东西,要成为有效赋值,左侧的参数必须包含右侧的参数。包含主要控制泛型子类型,因为“具体”泛型类型是不变的。
您可能熟悉以下想法:
List<Dog>
不是一个List<Animal>
- 但 a
List<Dog>
是 a List<? extends Animal>
。
好吧,后者是正确的,因为? extends Animal
contains Dog
。
所以问题变成了“类型参数是否List<?>
包含原始类型参数List
”?答案是否定的:虽然List<?>
是 的子类型List
,但这种关系不适用于类型参数。
没有特殊的规则使它成为真的:List<List<?>>
不是 的子类型,List<List>
原因基本相同List<Dog>
不是 的子类型List<Animal>
。
所以因为List<List>
不是 的子类型List<List<?>>
,所以赋值是无效的。同样,您不能执行直接缩小转换强制转换,因为List<List>
它不是List<List<?>>
两者的超类型。
要进行分配,您仍然可以应用演员表。在我看来,有三种方法可以做到这一点。
// 1. raw type
@SuppressWarnings("unchecked")
List<List<?>> list0 = (List) api();
// 2. slightly safer
@SuppressWarnings({"unchecked", "rawtypes"})
List<List<?>> list1 = (List<List<?>>) (List<? extends List>) api();
// 3. avoids a raw type warning
@SuppressWarnings("unchecked")
List<List<?>> list2 = (List<List<?>>) (List<? super List<?>>) api();
(你可以用JAXBElement
内部代替List
。)
您的这种转换的用例应该是安全的,因为List<List<?>>
它是一种比List<List>
.
原始类型语句是一个扩大的演员然后未经检查的分配。这是可行的,因为如上所示,任何参数化类型都可以转换为其原始类型,反之亦然。
稍微安全一点的语句(这样命名是因为它丢失的类型信息较少)是一个加宽转换然后缩小转换。这通过转换为一个常见的超类型来工作:
List<? extends List>
╱ ╲
List<List<?>> List<List>
有界通配符允许考虑通过包含进行子类型化的类型参数。
可以用传递性证明List<? extends List>
被认为是 的超类型的事实:List<List<?>>
? extends List
contains ? extends List<?>
,因为List
是 的超类型List<?>
。
? extends List<?>
包含List<?>
.
因此? extends List
包含List<?>
。
(即,List<? extends List> :> List<? extends List<?>> :> List<List<?>>
。)
第三个示例的工作方式与第二个示例类似,通过强制转换为一个常见的超类型List<? super List<?>>
。由于它不使用原始类型,我们可以减少一个警告。
这里的非技术总结是规范暗示 和 之间既没有子类型也没有超类型List<List>
关系List<List<?>>
。
虽然从List<List>
to转换List<List<?>>
应该是安全的,但这是不允许的。(这是安全的,因为两者都是 aList
可以存储任何类型的List
,但是 aList<List<?>>
对其元素在检索后的使用方式施加了更多限制。)
不幸的是,除了原始类型很奇怪并且它们的使用存在问题之外,没有任何实际原因无法编译。