在下面的示例中,如果列表中有多个类型,则编译正常,但如果我有一个元素,则会选择不再可分配的其他类型。
// 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>>
为什么有两个元素编译但一个元素没有?
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
这也不编译(它甚至不会解析)
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);
编辑:基于Marko的例子。在这四个示例中,一个不编译,其余的生成相同类型的相同列表。
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));
答案 0 :(得分:14)
有趣的问题。我认为这是怎么回事。当您有两个显示的元素时,asList
的返回类型是所有参数的最具体类型,在您的第一个示例中为List<Reference>
。这与List<? extends Reference>
分配兼容。当你有一个参数时,返回类型是参数的特定类型,它不是赋值兼容的,因为泛型不是协变的。
答案 1 :(得分:9)
考虑
// ok
List<Object> list3 = Arrays.asList(new Object(), new String());
// fail
List<Object> list4 = Arrays.asList(new String());
第二个示例尝试将List<String>
分配给失败的List<Object>
。
第二个例子可以工作,如果javac查看周围的上下文,考虑目标类型,并推断出T=Object
可以在这里工作。 Java 8可能会这样做(我不确定)
只有在一种情况下,javac(java 5)才会使用上下文信息进行类型推断,请参阅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
无法从参数类型中推断出来,并且方法结果位于赋值上下文中,因此javac会尝试按目标类型推断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
答案 2 :(得分:4)
解释这种行为有两个部分:
asList
的签名是
<T> List<T> asList(T... a)
这意味着必须将所有参数合并为单个类型T
,这是所有参数类型所共有的最具体的类型。在这种特殊情况下,我们有
asList(WeakReference.class) -> List<Class<WeakReference>>
和
asList(WeakReference.class, SoftReference.class)
-> List<Class<? extends Reference>>
这两个都很明显。
现在,为什么我们不能将类型List<Class<WeakReference>>
的第一个表达式分配给List<Class<? extends Reference>>
类型的变量?了解规则必须如此的最佳方式是矛盾证明。请考虑以下事项:
List<Class<? extends Reference>>
有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);
这会导致明显违反类型安全。
答案 3 :(得分:2)
这很有趣:
where INT#1 is an intersection type:
INT#1 extends AbstractList,Cloneable,Serializable
也许这是(某些)问题的原因?
可能无法唯一确定元素的交集类型。声明自己的列表MyList<T> implements List<T>
时,数组的交集类型将确定为List<T>
。
使用Arrays.<Class<? extends List>>asList(ArrayList.class, LinkedList.class);
时,明确声明了'intersection'类型(如List
),并且不需要由编译器推断。
除此之外,我相信Ted Hopp说的另一种情况是正确的。
编辑:
之间的区别
List<Class<? extends Reference>> list2 = Arrays.asList(WeakReference.class);
和
List<Class<? extends Reference>> list3 = Arrays.<Class<? extends Reference>>asList(WeakReference.class);
可能是编译器确定新列表类型的时间点:我认为在考虑赋值之前需要确定列表的泛型类型。为此,它需要信息来推断新的列表的类型,而没有关于赋值。这可能导致上述两个语句创建两种不同类型的列表,从而导致观察到的行为。