无法从列表< List>到List< List<?>> [英] Cannot convert from List<List> to List<List<?>>

查看:146
本文介绍了无法从列表< List>到List< List<?>>的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

原始列表转换为列表<?> 很好。为什么原始列表的列表不能转换为列表<?>

A raw list converts to List<?> just fine. Why can't a list of raw lists convert to a list of 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;
}

背景 http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem\"> xy问题):

Backstory (to mitigate the xy problem):

API我使用返回 List< JAXBElement> 。我偶然知道它总是 List< JAXBElement< String>> 。我计划循环并构建自己的 List< String> ,但是我试图修复(但不是抑制)原始类型编译器警告,当我写 List< JAXBElement>  raw  =  api();

An API I'm using returns List<JAXBElement>. I happen to know that it is always List<JAXBElement<String>>. I plan to loop and build my own List<String>, but I was trying to fix (but not suppress) the raw type compiler warning when I write List<JAXBElement> raw = api();.

我试过:

List<JAXBElement<?>> raw = api();
List<JAXBElement<?>> raw = (List<JAXBElement<?>>) api();

但这些会导致类型不匹配错误。

but these give the type mismatch error.

有趣的是,这不会产生任何警告或错误:

Interestingly, this gives no warning or error:

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;

首先让我们来解释为什么这些实际上是不相关的赋值。也就是说,它们由不同的规则定义。

First let's sort out why these are actually unrelated assignments. That is to say, they are defined by different rules.

#1称为 未选中的转化


原始类或接口类型中存在未检查的转换§4.8 G G 1 ,...,T n>

There is an unchecked conversion from the raw class or interface type (§4.8) G to any parameterized type of the form G<T1,...,Tn>.

具体来说,这是一个 作业上下文

Specifically it is a special case of an assignment context just for this scenario:


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

If, after [other possible conversions] have been applied, the resulting type is a raw type, an unchecked conversion may then be applied.

#2需要参考类型转换;然而问题是它不是一个 拓宽转换 (这是在不使用强制转换的情况下隐式允许的引用转化)。

#2 requires a reference type conversion; however the problem with it is that it is not a widening conversion (which is the kind of reference conversion that would be implicitly allowed without a cast).

这是为什么?嗯,这特别受泛型子类型规则,更具体地说,这个项目符号:

Why is that? Well, this is specifically governed by the rules of generic subtyping and more specifically this bullet point:


给定一个泛型类型声明 C ; f 1 ,...,F n> n <> em>> 0)参数化类型 C ,...,T > 的直接超类型 ,其中 T i (1≤ i n )所有以下内容:

Given a generic type declaration C<F1,...,Fn> (n > 0), the direct supertypes of the parameterized type C<T1,...,Tn>, where Ti (1 ≤ in) is a type, are all of the following:


  • C ,...,S n > ,其中 S i 包含

  • C<S1,...,Sn>, where Si contains Ti (1 ≤ in).

这指的是JLS调用 containment ,要作为有效的分配,左侧的参数必须包含右侧的参数。遏制在很大程度上决定了通用子类型,因为具体通用类型不变

This refers us to something the JLS calls containment, where to be a valid assignment, the arguments of the left-hand side must contain the arguments of the right-hand side. Containment largely governs generic subtyping since "concrete" generic types are invariant.

您可能已经熟悉以下想法:

You may be familiar with the ideas that:


  • a 列表< Dog> 不是 List< Animal>

  • 列表< Dog> 列表<?& extends  Animal>

  • a List<Dog> is not a List<Animal>
  • but a List<Dog> is a List<? extends Animal>.

好吧,后者是真的,因为 ; extends  Animal 包含 Dog

Well, the latter is true because ? extends Animal contains Dog.

问题变成了一个列表<?>包含一个原始列表?答案是否定的:虽然 List List 的子类型,类型参数。

So the question becomes "does a List<?> contain a raw List"? And the answer is no: although List<?> is a subtype of List, this relationship does not hold for type arguments.

没有特殊规则使其成立: List< List<?>> 由于基本上相同的原因 List< Dog> 不是 List< List> 的子类型。 code> List< Animal>

There is no special rule that makes it true: List<List<?>> is not a subtype of List<List> for essentially the same reason List<Dog> is not a subtype of List<Animal>.

因为 List< List> 不是 List< List<?>> 的子类型,则赋值无效。同样,您无法直接 列表< List> 不是列表< List<?>的超类型,所以缩小转换 >

So because List<List> is not a subtype of List<List<?>>, the assignment is invalid. Similarly, you cannot perform a direct narrowing conversion cast because List<List> is not a supertype of List<List<?>> either.

要进行分配,您仍然可以应用投射。有三种方法可以做到对我来说是合理的。

To make the assignment you can still apply a cast. There are three ways to do it that seem reasonable to me.

// 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. added 5/23/16: this version doesn't cause a raw type warning
@SuppressWarnings("unchecked")
List<List<?>> list2 = (List<List<?>>) (List<? super List<?>>) api();

(您可以用 JAXBElement 列表。)

您的用例此安装应该是安全的,因为 List<列表<?>> 是比 List< List> 更受限制的类型。

Your use-case for this casting should be safe because List<List<?>> is a more restrictive type than List<List>.


  • 原始类型语句是一个扩展转换,然后取消选中分配。这是因为,如上所示,任何参数化类型都可以转换为其原始类型,反之亦然。

  • The raw type statement is a widening cast then unchecked assignment. This works because, as shown above, any parameterized type can be converted to its raw type and vice-versa.

>语句(因为它丢失较少的类型信息而被复制)是一个扩展的cast,然后narrowing cast。这可以通过转换为常用的父类型来实现:

The slightly safer statement (dubbed so because it loses less type information) is a widening cast then narrowing cast. This works by casting to a common supertype:

    List<? extends List>
        ╱         ╲
List<List<?>>     List<List>

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

The bounded wildcard allows the type arguments to be considered for subtyping via containment.

作为一个侧面的注释,在这个答案的以前的版本中,我很犹豫为什么 List<?  extend  List> 被认为是 List< List<?> 的超类型,但这可以通过传递属性来证明:

And as a side note, in previous revisions of this answer I was hesitant about why List<? extends List> is considered a supertype of List<List<?>>, but this can be proven through the transitive property:


  1. ? 扩展 列表包含因为列表列表<?> 的超类型,所以扩展列<?> 。

  1. ? extends List contains ? extends List<?> because List is a supertype of List<?>.

? 延伸<?> 包含列表<?>

也就是说,列表<?  extend 列表> :> 列表<?  ; List<?>> :> 列表< List<?>< / code>。

第三个例子( added 5/23/16 )的工作方式与第二个例子相同,转换为一个常用的超类型 List<?& super  List<?>> 。因为它不使用原始类型,所以我们可以减少一个警告。

The third example (added 5/23/16) works pretty much the same way as the second example, by casting to a common supertype List<? super List<?>>. It's just a little nicer in that since it doesn't use a raw type, we can suppress one less warning.

这里的非技术摘要是规范意味着 List< List> 之间既没有子类型也没有超类型关系, 列表< List<?>>

The non-technical summary here is that the specification implies that there is neither subtype nor supertype relationship between List<List> and List<List<?>>.

; 列表< List<?>> 应为安全,不允许。 (它是安全的,因为它们都是一个 List ,它可以存储任何类型的 List ,但列表< List<?>> 对检索后的元素如何使用施加了更多限制。)

Although converting from List<List> to List<List<?>> should be safe, it is not allowed. (It's safe because both are a List that can store any kind of List, but a List<List<?>> imposes more restrictions on how its elements can be used after they are retrieved.)

没有实际的原因,这不能编译,除了原始类型是奇怪的,它们的使用是有问题的。

There is unfortunately no practical reason this fails to compile except that raw types are strange and usage of them is problematic.

这篇关于无法从列表&lt; List&gt;到List&lt; List&lt;?&gt;&gt;的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆