java 与 Arrays.asList() 不兼容的类型

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14091771/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 15:01:59  来源:igfitidea点击:

Incompatible type with Arrays.asList()

javagenerics

提问by Peter Lawrey

In the following example, if I have multiple types in the list it compiles ok, but if I have one element, it chooses a different type which is no longer assignable.

在下面的例子中,如果我在列表中有多种类型,它可以编译,但如果我有一个元素,它会选择一种不再可分配的不同类型。

// compiles fine
List<Class<? extends Reference>> list = Arrays.asList(SoftReference.class, WeakReference.class);
// but take an element away and it no longer compiles.
List<Class<? extends Reference>> list2 = Arrays.asList(WeakReference.class);
// without giving the specific type desired.
List<Class<? extends Reference>> list3 = Arrays.<Class<? extends Reference>>asList(WeakReference.class);

I am sure there is a logical explaination for this, but it escapes me.

我确信对此有一个合乎逻辑的解释,但它让我无法理解。

    Error:Error:line (30)error: incompatible types
required: List<Class<? extends Reference>>
found:    List<Class<WeakReference>>

Why does having two elements compile but one element does not?

为什么有两个元素可以编译而一个元素不能?

BTW: It is hard to find a simple example, if you try

BTW:很难找到一个简单的例子,如果你尝试

List<Class<? extends List>> list = Arrays.asList(ArrayList.class, LinkedList.class);

    Error:Error:line (28)error: incompatible types
required: List<Class<? extends List>>
found:    List<Class<? extends INT#1>>
where INT#1 is an intersection type:
INT#1 extends AbstractList,Cloneable,Serializable

This doesn't compile either (it won't even parse)

这也不会编译(它甚至不会解析)

List<Class<? extends AbstractList & Cloneable & Serializable>> list = Arrays.asList(ArrayList.class, LinkedList.class);

Error:Error:line (30)error: > expected
Error:Error:line (30)error: ';' expected

but this compiles fine

但这编译得很好

static abstract class MyList<T> implements List<T> { }
List<Class<? extends List>> list = 
        Arrays.asList(ArrayList.class, LinkedList.class, MyList.class);
List<Class<? extends List>> list = 
        Arrays.<Class<? extends List>>asList(ArrayList.class, LinkedList.class);


EDIT: Based on Marko's example. In these four example, one doesn't compile, the rest produce the same list of the same type.

编辑:基于马尔科的例子。在这四个示例中,一个不编译,其余生成相同类型的相同列表。

List<Class<? extends Reference>> list = new ArrayList<>();
list.add(SoftReference.class);
list.add(WeakReference.class);
list.add(PhantomReference.class);

List<Class<? extends Reference>> list = new ArrayList<>(
     Arrays.asList(SoftReference.class));
list.add(WeakReference.class);
list.add(PhantomReference.class);

List<Class<? extends Reference>> list = new ArrayList<>(
     Arrays.asList(SoftReference.class, WeakReference.class));
list.add(PhantomReference.class);

List<Class<? extends Reference>> list = new ArrayList<>(
     Arrays.asList(SoftReference.class, WeakReference.class, PhantomReference.class));

采纳答案by Ted Hopp

Interesting problem. I think that what's going on is this. When you have two elements like you show, the return type from asListis most specific type of all the arguments, which in your first example is List<Reference>. This is assignment-compatible with List<? extends Reference>. When you have a single argument, the return type is the specific type of the argument, which is not assignment-compatible because generics are not covariant.

有趣的问题。我认为正在发生的事情是这样的。当你有两个像你展示的元素时,返回类型 fromasList是所有参数中最具体的类型,在你的第一个例子中是List<Reference>. 这与赋值兼容List<? extends Reference>。当您只有一个参数时,返回类型是参数的特定类型,这与赋值不兼容,因为泛型不是协变的。

回答by irreputable

Consider

考虑

    // ok
    List<Object> list3 = Arrays.asList(new Object(), new String());
    // fail
    List<Object> list4 = Arrays.asList(new String());

The 2nd example tries to assigne a List<String>to a List<Object>, which fails.

第二个示例尝试将 a 分配List<String>给 a List<Object>,但失败了。

The 2nd example could work, if javac looks at the surrounding context, takes into account the target type, and deduce that T=Objectwould work here. Java 8 will probably do that (I'm not sure)

第二个示例可以工作,如果 javac 查看周围的上下文,考虑到目标类型,并推断出T=Object在这里可以工作。Java 8 可能会这样做(我不确定)

Only in one situation, javac (of java 5) will use contextual info for type inference, see http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.12.2.8

只有在一种情况下,javac(java 5)将使用上下文信息进行类型推断,请参阅http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.12。 2.8

We can take advantage of that to make a workaround

我们可以利用这一点来解决

public static <R, T extends R> List<R> toList(T... elements)
{
    return Arrays.asList((R[])elements);
}

Now they can be compiled:

现在可以编译它们:

    List<Object> list4 = toList(new String());

    List<Class<? extends Reference>> list = toList(SoftReference.class, WeakReference.class);

    List<Class<? extends Reference>> list2 = toList(WeakReference.class);

This is because Rcannot be inferred from argument types, and the method result is in an assignment context, so javac tries to infer Rby the target type.

这是因为R无法从参数类型推断,并且方法结果在赋值上下文中,因此 javac 尝试R通过目标类型推断。

This works in assignment, or in a return statement

这适用于赋值或 return 语句

List<Class<? extends Reference>> foo()
{
    return toList(WeakReference.class);  // "subject to assignment conversion"
}

It won't work otherwise

否则它不会工作

void bar(List<Class<? extends Reference>> list){...}

bar( toList(WeakReference.class) ); // fail; R not inferred

回答by Marko Topolnik

There are two parts to the explanation of this behavior:

这种行为的解释有两个部分:

  1. How does the type of the right-hand side change with changing arguments?
  2. Why are some of the RHS types incompatible with the LHS type?
  1. 右边的类型如何随着参数的变化而变化?
  2. 为什么有些 RHS 类型与 LHS 类型不兼容?

1. The Right-Hand Side

1. 右手边

The signature of asListis

的签名asList

<T> List<T> asList(T... a)

This means that all the arguments must be conflated into a single type T, which is the most specific type common to the types of all arguments. In this particular case, we have

这意味着所有参数必须合并为一个类型T,这是所有参数类型通用最具体的类型。在这种特殊情况下,我们有

asList(WeakReference.class) -> List<Class<WeakReference>>

and

asList(WeakReference.class, SoftReference.class) 
   -> List<Class<? extends Reference>>

Both of these are obvious enough.

这两点已经足够明显了。

2. The Left-Hand Side

2. 左侧

Now, why can't we assign the first expression, of type List<Class<WeakReference>>, to a variable of type List<Class<? extends Reference>>? The best way to understand why the rules must be so is proof by contradiction. Consider the following:

现在,为什么我们不能将 type 的第一个表达式分配给 typeList<Class<WeakReference>>的变量List<Class<? extends Reference>>?理解为什么规则必须如此的最好方法是通过矛盾来证明。考虑以下:

  • List<Class<? extends Reference>>has add(Class<? extends Reference>)
  • List<Class<WeakReference>>has add(Class<WeakReference>).
  • List<Class<? extends Reference>>拥有 add(Class<? extends Reference>)
  • List<Class<WeakReference>>add(Class<WeakReference>).

Now, if Java allowed you to assign one to the other:

现在,如果 Java 允许您将一个分配给另一个:

List<Class<WeakReference>> lw = new ArrayList<>();
List<Class<? extends Reference>> lq = lw;
lq.add(PhantomReference.class);

it would result in a clear violation of type safety.

这将导致明显违反类型安全。

回答by JimmyB

This is interesting:

这很有趣:

where INT#1 is an intersection type:
INT#1 extends AbstractList,Cloneable,Serializable

Maybe that is the cause for (some of the) issues?

也许这就是(某些)问题的原因?

The intersectiontype of the elements may not be uniquely determined. When you declare your own list MyList<T> implements List<T>, the intersection type of the array is determined as List<T>.

元素的交集类型可能不是唯一确定的。当您声明自己的列表时MyList<T> implements List<T>,数组的交集类型被确定为List<T>

When using Arrays.<Class<? extends List>>asList(ArrayList.class, LinkedList.class);the 'intersection' type is explicitly stated (as List) and does not need to be inferred by the compiler.

使用Arrays.<Class<? extends List>>asList(ArrayList.class, LinkedList.class);'intersection' 类型时明确声明(如List)并且不需要由编译器推断。

Besides that, I believe what Ted Hopp said is correct for the other case.

除此之外,我相信 Ted Hopp 所说的对于另一个案例是正确的。

EDIT:

编辑:

The difference between

和...之间的不同

List<Class<? extends Reference>> list2 = Arrays.asList(WeakReference.class);

and

List<Class<? extends Reference>> list3 = Arrays.<Class<? extends Reference>>asList(WeakReference.class);

may be the point in time when the compiler determines the new list's type: I figure it needs to determine the generic type of the list beforeconsidering the assignment. For this it takes the information it has to infer the type of the new list withoutregard to the assignment. This may cause two different types of list to be created by the two statements above, resulting in the observed behavior.

可能是编译器确定新列表类型的时间点:我认为它需要考虑赋值之前确定列表的泛型类型。为此,它需要信息来推断新列表的类型,而不考虑分配。这可能会导致上述两个语句创建两种不同类型的列表,从而导致观察到的行为。