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

Pet*_*rey 36 java generics

在下面的示例中,如果列表中有多个类型,则编译正常,但如果我有一个元素,则会选择不再可分配的其他类型.

// 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);
Run Code Online (Sandbox Code Playgroud)

我确信这有一个合乎逻辑的解释,但它逃脱了我.

    Error:Error:line (30)error: incompatible types
required: List<Class<? extends Reference>>
found:    List<Class<WeakReference>>
Run Code Online (Sandbox Code Playgroud)

为什么有两个元素编译但一个元素没有?

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

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
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

但是编译得很好

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);
Run Code Online (Sandbox Code Playgroud)

编辑:基于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));
Run Code Online (Sandbox Code Playgroud)

Ted*_*opp 14

有趣的问题.我认为这是怎么回事.当你有两个像你显示的元素时,返回类型asList是所有参数的最具体类型,在你的第一个例子中List<Reference>.这与分配兼容List<? extends Reference>.当您有一个参数时,返回类型是参数的特定类型,它不是赋值兼容的,因为泛型不是协变的.

  • @PeterLawrey - 我说错了.规则是相同的,但案例不同,因此规则的不同方面适用.每个实际参数都被分配给一个参数,该参数是所有实际参数的交集类型.几乎根据交集类型的定义,这不会导致问题.另一方面,赋值语句将交集类型分配给代码指定的泛型类型.那时可能会出现问题(在这种情况下,因为泛型类型不是协变的). (3认同)

irr*_*ble 9

考虑

    // ok
    List<Object> list3 = Arrays.asList(new Object(), new String());
    // fail
    List<Object> list4 = Arrays.asList(new String());
Run Code Online (Sandbox Code Playgroud)

第二个例子尝试将a分配List<String>给a 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);
}
Run Code Online (Sandbox Code Playgroud)

现在他们可以编译:

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

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

    List<Class<? extends Reference>> list2 = toList(WeakReference.class);
Run Code Online (Sandbox Code Playgroud)

这是因为R无法从参数类型推断出,并且方法结果位于赋值上下文中,因此javac会尝试R按目标类型进行推断.

这适用于赋值或返回语句

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

否则它将不起作用

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

bar( toList(WeakReference.class) ); // fail; R not inferred
Run Code Online (Sandbox Code Playgroud)

  • +1,这是正确的答案.关键是在JLS§15.12.2.8的第一行:"**如果没有从实际参数的类型中推断出任何方法的类型参数,**现在推断它们如下......"返回列表的类型*是从`Arrays.asList()`的实际参数中推断出来的,上下文推理的规则不适用. (2认同)