当前位置: 首页 > 面试题库 >

无法从列表转换 列出>

经兴安
2023-03-14
问题内容

原始列表将转换为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;
}

背景故事 (以减轻xy问题):

我正在使用return的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()) {
    // ...
}

问题答案:

// #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需要引用类型转换;但是,它的问题在于它不是
扩展转换
(这是一种无需转换就可以隐式允许的引用转换)。

这是为什么?好吧,这特别受通用子类型的规则支配,更具体地讲,要点如下:

给出一个通用的类型声明( Ñ > 0),则 直接超 参数化类型的,其中,(1≤ Ñ
)是一种类型的,是以下所有:C<F1,...,Fn> C<T1,...,Tn>``Ti

  • C<S1,...,Sn>,其中包含(1≤ Ñ )。Si``Ti

这就是JLS所称的
containment
,这是有效的赋值,左侧的参数必须 包含
右侧的参数。由于“具体”泛型类型是不变的,因此包含在很大程度上决定了泛型子类型。

您可能熟悉以下想法:

  • 一个List<Dog>不是一个List<Animal>
  • 但是a List<Dog>是一个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<? extends List>被认为是超类的事实List<List<?>>可以通过传递性来证明:

1. `? extends List`包含`? extends List<?>`,因为`List`是的超类型`List<?>`。

2. `? extends List<?>`包含`List<?>`。

3. 因此`? extends List`包含`List<?>`。

(即List<? extends List> :> List<? extends List<?>> :> List<List<?>>。)

  • 第三个示例的工作方式与第二个示例类似,通过强制转换为通用超类型List<? super List<?>>。由于它不使用原始类型,因此我们可以减少一个警告。

这里的非技术性总结是,规范意味着有间既不亚型也不超关系List<List>List<List<?>>

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

不幸的是,除了原始类型是奇怪的而且它们的使用有问题之外,没有任何实际原因无法编译。



 类似资料:
  • 我试图通过将对象传递给“验证器”来验证库中的结果,其中验证器验证结果是否为空,并随后将传递的对象返回给调用方。例如: 调用方将调用以下内容: 我知道参数绑定发生在运行时,这就是编译器抛出这个错误的原因。我正试图避免使用显式类型创建多个方法,而宁愿捕获实现的所有类型。 在不上传或创建多个显式类型重载方法的情况下,有没有其他方法可以做到这一点?

  • 问题内容: 我试图将字符串列表从我的Java控制器传递到scala模板。 这是视图代码: 这是临时代码: 我遇到的错误(在Typesafe Activation Compile页面中): 类索引中的render方法不能应用于给定的类型; 必需:scala.collection.immutable.List 找到:java.util.List 原因:实际参数java.util.List无法通过方法调

  • 问题内容: 我已经编写了此函数,用于将元组列表转换为列表列表。有没有更优雅的/ Pythonic的方式来做到这一点? 问题答案: 您可以使用列表推导:

  • 我正在尝试使用 jolt 转换 JSON,而无需添加新项目以及从键值中删除空格。此外,我想知道如何映射到当前列表。 输入: 预期产出: 规格已尝试:

  • 问题内容: List myclassList = (List ) rs.get(); 我不明白为什么这段代码会产生这个: MyClass没有实现Comparable。我只想使用Set来过滤List的唯一元素,因为我的List包含不必要的重复项。 问题答案: 是否或类似的东西? 如果没有,那就是为什么。 为此,您必须制作元素或提供一个。否则将无法运行,因为它将不知道如何对元素进行排序。 请记住,,因

  • 问题内容: 将a 转换为同时保持Queue顺序的最快方法是什么? 问题答案: 最快的方法是首先使用LinkedList,它可用作列表或队列。 否则您需要复印 注意:处理PriorityQueue时,请使用循环,轮询每个元素并添加到列表中。要列出的PriorityQueue不维护堆顺序。