与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
这个行为的解释有两个部分:
- 右边的types如何随着参数的变化而变化?
- 为什么一些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>>
hasadd(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,导致观察到的行为。