无法从List 转换为List <List >
原始列表转换为List
就好了。 为什么原始列表列表不能转换为List
?
{ // works List raw = null; List wild = raw; } { // Type mismatch: cannot convert from List to List<List> List raw = null; List<List> wild = raw; }
背景故事 (缓解xy问题 ):
我正在使用的API返回List
。 我碰巧知道它始终是List<JAXBElement>
。 我打算循环并构建自己的List
,但是当我编写List raw = api();
时,我试图修复(但不是抑制)原始类型编译器警告List raw = api();
。
我试过了:
List<JAXBElement> raw = api(); List<JAXBElement> raw = (List<JAXBElement>) api();
但这些给出了类型不匹配错误。
有趣的是,这没有任何警告或错误:
for (JAXBElement e : api()) { // ... }
// #1 (does compile) List raw = null; List> wild = raw; // #2 (doesn't compile) List raw = null; List> wild = raw;
首先让我们理清为什么这些实际上是无关的任务。 也就是说,它们受不同规则的约束。
#1被称为未经检查的转换 :
从原始类或接口类型( §4.8 )
G
到G
forms的任何参数化类型都有未经检查的转换 。1 ,...,T n >
具体来说,这是一个仅适用于此场景的赋值上下文的特例:
如果在应用[其他可能的转换]之后,结果类型是原始类型,则可以应用未经检查的转换。
#2需要引用类型转换; 然而,它的问题在于它不是一个加宽的转换 (这是一种在没有强制转换的情况下隐式允许的引用转换)。
这是为什么? 嗯,这是由通用子类型的规则特别管理,更具体地说,这个要点:
给定generics类型声明
C
( n > 0),参数化类型1 ,...,F n > C
的直接超类型,其中1 ,...,T n > T i
(1≤i) ≤n)是一种类型,全部如下:
C
,其中1 ,...,S n >S i
包含T i
(1≤i≤n)。
这将我们引用到JLS调用包含的东西,在哪里是有效的赋值,左侧的参数必须包含右侧的参数。 遏制主要控制通用子类型,因为“具体”generics类型是不变的 。
您可能熟悉以下想法:
-
List
不是List
- 但
List
是一个List extends Animal>
List extends Animal>
。
嗯,后者是真的,因为? extends Animal
? extends Animal
包含 Dog
。
所以问题变成“类型参数List>
包含原始类型参数List
” ? 答案是否定的:虽然List>
是List
的子类型,但这种关系不适用于类型参数。
没有特殊规则使其成立: List
不是>
List
的子类型,原因与List
不是List
的子类型基本相同。
因为List
不是List
的子类型,所以赋值无效。 同样,您不能执行直接缩小转换,因为>
List
也不是List
的超类型。 >
要进行分配,您仍然可以应用演员表。 有三种方法可以让我觉得合理。
// 1. raw type @SuppressWarnings("unchecked") List> list0 = (List) api(); // 2. slightly safer @SuppressWarnings({"unchecked", "rawtypes"}) List> list1 = (List>) (List extends List>) api(); // 3. avoids a raw type warning @SuppressWarnings("unchecked") List> list2 = (List>) (List super List>>) api();
(您可以将JAXBElement
替换为内部List
。)
这个转换的用例应该是安全的,因为List
是比>
List
更具限制性的类型。
-
原始类型语句是扩展的强制转换,然后是未选中的赋值。 这是有效的,因为如上所示,任何参数化类型都可以转换为其原始类型,反之亦然。
-
稍微更安全的声明(因为它丢失较少的类型信息而命名)是一个扩大的演员然后缩小演员。 这通过转换为常见的超类型来工作:
List extends List> ╱ ╲ List
- > List
有界通配符允许通过包含考虑类型参数进行子类型化。
List extends List>
List extends List>
被认为是List
的超类型,可以通过传递性certificate:- >
-
? extends List
? extends List
包含? extends List>
? extends List>
,因为List
是List>
的超类型。 -
? extends List>
? extends List>
包含List>
。 -
因此
? extends List
? extends List
包含List>
。
(即
List extends List> :> List extends List>> :> List
。)- >
-
-
第三个示例以类似于第二个示例的方式工作,通过转换为常见的超类型
List super List>>
List super List>>
。 由于它不使用原始类型,我们可以抑制少一个警告。
这里的非技术性摘要是规范暗示List
和List
之间既没有子类型也没有超类型关系。 >
虽然从List
转换为List
应该是安全的 ,但是不允许。 (这是安全的,因为它们都是可以存储任何类型>
List
,但是List
对它们的元素在检索后如何使用有更多的限制。) >
遗憾的是,没有实际的原因,除了原始类型很奇怪并且使用它们存在问题之外,它无法编译。
您无法直接分配或转换它 ,因为原始类型List
与 List>
。
使用List
类型时,将忽略检查,您可以使用任何类型的通用方法。 使用List>
, 编译器不允许您使用具有generics参数的方法 。
因此,您可以忽略警告:
@SuppressWarnings("rawtypes")
和/或使用解决方法明确地转换它:
List> raw = (List>) ((Object)api());
如果您只想删除警告,可以使用@SuppressWarnings(“rawtypes”)。
基本上问题是编译器将rawtype视为原始Object以前的generics所以……“旧对象”不是“通用对象”所以……你不能强制转换它们。
请阅读官方文档: http : //docs.oracle.com/javase/tutorial/java/generics/rawTypes.html
但是,如果将原始类型分配给参数化类型,则会收到警告:
Box rawBox = new Box(); // rawBox是Box Box的原始类型intBox = rawBox; //警告:未经检查的转换如果使用原始类型调用相应generics类型中定义的generics方法,也会收到警告:
Box stringBox = new Box <>(); Box rawBox = stringBox; rawBox.set(8); // warning:unchecked invocation to set(T)警告显示原始类型绕过泛型类型检查,将不安全代码的捕获推迟到运行时。 因此,您应该避免使用原始类型。