无法从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 ) GG1 ,...,T n >forms的任何参数化类型都有未经检查的转换

具体来说,这是一个仅适用于此场景的赋值上下文的特例:

如果在应用[其他可能的转换]之后,结果类型是原始类型,则可以应用未经检查的转换。

#2需要引用类型转换; 然而,它的问题在于它不是一个加宽的转换 (这是一种在没有强制转换的情况下隐式允许的引用转换)。

这是为什么? 嗯,这是由通用子类型的规则特别管理,更具体地说,这个要点:

给定generics类型声明C1 ,...,F n >n > 0),参数化类型C1 ,...,T n >直接超类型,其中T i (1≤i) ≤n)是一种类型,全部如下:

  • C1 ,...,S n > ,其中S i包含T i (1≤i≤n)。

这将我们引用到JLS调用包含的东西,在哪里是有效的赋值,左侧的参数必须包含右侧的参数。 遏制主要控制通用子类型,因为“具体”generics类型是不变的 。

您可能熟悉以下想法:

  • List不是List
  • List是一个List List

嗯,后者是真的,因为? extends Animal ? extends Animal 包含 Dog

所以问题变成“类型参数List包含原始类型参数List ? 答案是否定的:虽然ListList的子类型,但这种关系不适用于类型参数。

没有特殊规则使其成立: 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) api(); // 3. avoids a raw type warning @SuppressWarnings("unchecked") List> list2 = (List>) (List>) api(); 

(您可以将JAXBElement替换为内部List 。)

这个转换的用例应该是安全的,因为List>是比List更具限制性的类型。

  • 原始类型语句是扩展的强制转换,然后是未选中的赋值。 这是有效的,因为如上所示,任何参数化类型都可以转换为其原始类型,反之亦然。

  • 稍微更安全的声明(因为它丢失较少的类型信息而命名)是一个扩大的演员然后缩小演员。 这通过转换为常见的超类型来工作:

      List ╱ ╲ List> List 

    有界通配符允许通过包含考虑类型参数进行子类型化。

    List List被认为是List>的超类型,可以通过传递性certificate:

    1. ? extends List ? extends List包含? extends List ? extends List ,因为ListList的超类型。

    2. ? extends List ? extends List包含List

    3. 因此? extends List ? extends List包含List

    (即List :> List> :> List> 。)

  • 第三个示例以类似于第二个示例的方式工作,通过转换为常见的超类型List> List> 。 由于它不使用原始类型,我们可以抑制少一个警告。


这里的非技术性摘要是规范暗示ListList>之间既没有子类型也没有超类型关系。

虽然从List转换为List>应该是安全的 ,但是不允许。 (这是安全的,因为它们都是可以存储任何类型List ,但是List>对它们的元素在检索后如何使用有更多的限制。)

遗憾的是,没有实际的原因,除了原始类型很奇怪并且使用它们存在问题之外,它无法编译。

您无法直接分配或转换它 ,因为原始类型ListList

使用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)警告显示原始类型绕过泛型类型检查,将不安全代码的捕获推迟到运行时。 因此,您应该避免使用原始类型。