与Arrays.asList()不兼容的types

在下面的例子中,如果我在列表中有多个types,它编译好,但是如果我有一个元素,它会select一个不能分配的types。

// 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); 

我相信这是一个合乎逻辑的解释,但它逃脱了我。

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

为什么有两个元素编译,但一个元素不是?

顺便说一句:如果你尝试,很难find一个简单的例子

 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 

这不会编译(它甚至不会parsing)

 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 

但是这个编译好

 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); 

编辑:基于马可的例子。 在这四个例子中,一个不编译,其余的产生相同types的相同列表。

 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)); 

有趣的问题。 我想这是怎么回事。 当你有两个元素像你显示的那样, asList的返回types是所有参数的最具体types,在你的第一个例子中是List<Reference> 。 这是分配兼容List<? extends Reference> List<? extends Reference> 。 当你有一个参数时,返回types是参数的具体types,它不是分配兼容的,因为generics不是协变的。

考虑

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

第二个例子试图将List<String>分配给List<Object> ,失败。

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

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

我们可以利用这个做一个解决方法

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

现在他们可以编译:

  List<Object> list4 = toList(new String()); List<Class<? extends Reference>> list = toList(SoftReference.class, WeakReference.class); List<Class<? extends Reference>> list2 = toList(WeakReference.class); 

这是因为R不能从参数types推断出来,并且方法结果是在赋值语境中,所以javac试图通过目标types来推断R

这在作业中,或在一个返回声明

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

否则将无法正常工作

 void bar(List<Class<? extends Reference>> list){...} bar( toList(WeakReference.class) ); // fail; R not inferred 

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

  1. 右边的types如何随着参数的变化而变化?
  2. 为什么一些RHStypes与LHStypes不兼容?

1.右手边

asList的签名是

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

这意味着所有的参数必须被合并成一个单一的typesT ,这是所有参数types所共有的最具体的types 。 在这个特殊的情况下,我们有

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

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

这两个都很明显。

2.左手边

现在,为什么我们不能将List<Class<WeakReference>>types的第一个expression式分配给List<Class<? extends Reference>>types的variablesList<Class<? extends Reference>> List<Class<? extends Reference>> ? 理解规则必须如此的最好方法就是通过矛盾来certificate。 考虑以下:

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

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

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

这会导致明显违反types安全。

这很有趣:

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

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

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

在使用Arrays.<Class<? extends List>>asList(ArrayList.class, LinkedList.class); Arrays.<Class<? extends List>>asList(ArrayList.class, LinkedList.class); “交集”types被明确声明(如List ),并不需要由编译器推断。

除此之外,我相信特德·霍普所说的对另外一种情况是正确的。

编辑:

和…之间的不同

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

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

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