// #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调用的包含,其中要成为有效的赋值,左侧的参数必须包含右侧的参数。包含在很大程度上控制泛型子类型,因为“具体”泛型类型是不变的。
您可能熟悉以下想法:
- a 不是 a
List<Dog>
List<Animal>
- 但 a 是 .
List<Dog>
List<? extends Animal>
好吧,后者是正确的,因为包含.? extends Animal
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
包含 ,因为 是 的超类型。? 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<?>>
尽管从 转换为 应该是安全的,但不允许这样做。(这是安全的,因为两者都是可以存储任何类型的 ,但是a对其元素在检索后如何使用它们施加了更多限制。List<List>
List<List<?>>
List
List
List<List<?>>
不幸的是,没有实际原因无法编译,除了原始类型很奇怪并且它们的使用存在问题。