原始列表转换为List<?>
就好了。为什么原始列表列表不能转换为List<?>
列表?
{ // works
List raw = null;
List<?> wild = raw;
}
{ // Type mismatch: cannot convert from List<List> to List<List<?>>
List<List> raw = null;
List<List<?>> wild = raw;
}
Backstory (以缓解the xy problem):
我正在使用的API返回List<JAXBElement>
。我碰巧知道它总是List<JAXBElement<String>>
。我打算循环并构建我自己的List<String>
,但是当我写List<JAXBElement> raw = api();
时,我试图修复(但不是抑制)原始类型编译器警告。
我试过了:
List<JAXBElement<?>> raw = api();
List<JAXBElement<?>> raw = (List<JAXBElement<?>>) api();
但这些会导致类型不匹配错误。
有趣的是,这没有任何警告或错误:
for (JAXBElement<?> e : api()) {
// ...
}
答案 0 :(得分:29)
// #1 (does compile)
List raw = null;
List<?> wild = raw;
// #2 (doesn't compile)
List<List> raw = null;
List<List<?>> wild = raw;
首先让我们理清为什么这些实际上是无关的任务。也就是说,它们受不同规则的约束。
#1称为unchecked conversion:
从原始类或接口类型(§4.8)
G
到G<T1,...,Tn>
形式的任何参数化类型都有未经检查的转化。
具体来说,这是一个assignment context的特例,仅适用于这种情况:
如果在应用[其他可能的转换]之后,结果类型是原始类型,则可以应用未经检查的转换。
#2需要引用类型转换;但是它的问题在于它不是widening conversion(这是一种在没有强制转换的情况下隐式允许的引用转换)。
为什么?嗯,这具体由rules of generic subtyping管理,更具体地说是这个要点:
给定泛型类型声明
C<F1,...,Fn>
( n &gt; 0),参数化类型C<T1,...,Tn>
的直接超类型,其中{{ 1}}(1≤ i ≤ n )是一种类型,全部如下:
Ti
,其中C<S1,...,Sn>
包含Si
(1≤ i ≤ n )。
这引用了我们JLS调用containment的内容,哪里是有效的赋值,左侧的参数必须包含右侧的参数。由于"concrete" generic types为invariant,因此遏制主要支配通用子类型。
您可能熟悉以下观点:
Ti
不是List<Dog>
List<Animal>
是List<Dog>
。嗯,后者是真的,因为List<? extends Animal>
包含 ? extends Animal
。
所以问题变成“类型参数Dog
是否包含原始类型参数List<?>
”?答案是否定的:虽然List
是List<?>
的子类型,但这种关系不适用于类型参数。
没有特殊规则可以使其成立:List
不是List<List<?>>
的子类型,原因基本相同List<List>
不是List<Dog>
的子类型。< / p>
因为List<Animal>
不是List<List>
的子类型,所以分配无效。同样,您无法执行直接narrowing conversion转换,因为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<?>>
转换为List<List>
应该安全,但不允许这样做。 (这是安全的,因为两者都是List<List<?>>
,可以存储任何类型的List
,但List
对其元素在检索后的使用方式施加了更多限制。)
遗憾的是,由于原始类型很奇怪并且使用它们存在问题,因此没有实际原因导致无法编译。
答案 1 :(得分:3)
您无法直接指定或投放,因为原始类型List
isn't the same为List<?>
。
使用List
时,忽略类型检查,您可以使用任何类型的通用方法。使用List<?>
时compiler won't let you use methods with generic parameters。
因此您可以忽略警告:
@SuppressWarnings("rawtypes")
和/或使用解决方法明确地转换它:
List<JAXBElement<String>> raw = (List<JAXBElement<String>>) ((Object)api());
答案 2 :(得分:0)
如果您只想删除警告,可以使用@SuppressWarnings(“rawtypes”)。
基本上问题是编译器将rawtype视为原始Object以前的泛型所以......“旧对象”不是“通用对象”所以......你不能强制转换它们。
从官方文档中阅读: http://docs.oracle.com/javase/tutorial/java/generics/rawTypes.html
但是如果你将一个原始类型分配给参数化类型,你会得到一个 警告:
Box rawBox = new Box(); // rawBox是Box的原始类型 Box intBox = rawBox; //警告:未经检查的转换你 如果使用原始类型来调用泛型方法,也会收到警告 在相应的泛型类型中定义:
Box stringBox = new Box&lt;&gt;(); Box rawBox = stringBox; rawBox.set(8); //警告:取消选中要调用的设置(T)警告 显示原始类型绕过泛型类型检查,推迟捕获 不安全的代码到运行时。因此,您应该避免使用原始 类型。