在Java中,为什么我们不能这样做:
List<List<? extends Number>> aList = new ArrayList<List<Number>>();
尽管这样没问题:
List<? extends Number> aList = new ArrayList<Number>();
编译器错误消息如下:
类型不匹配: 无法将ArrayList<List<Number>>转换为List<List<? extends Number>>
在Java中,如果Car
是Vehicle
的派生类,那么我们可以将所有的Car
视为Vehicle
; 一个Car
是一个Vehicle
。然而,List<Car>
不是List<Vehicle>
。我们说List<Car>
与List<Vehicle>
之间不具备协变性(non-covariant)。
当使用通配符?
时,Java要求您明确告诉它何时希望使用协变性和逆变性。看一下问题发生的位置:
List<List<? extends Number>> l = new ArrayList<List<Number>>();
// ---------------- ------
//
// "? extends Number" matched by "Number". Success!
内部的List<? extends Number>
之所以可行,是因为Number
确实扩展了Number
,因此它与"? extends Number
"匹配。 目前为止,一切正常。接下来呢?
List<List<? extends Number>> l = new ArrayList<List<Number>>();
// ---------------------- ------------
//
// "List<? extends Number>" not matched by "List<Number>". These are
// different types and covariance is not specified with a wildcard.
// Failure.
然而,组合内部类型参数 List<? extends Number>
与 List<Number>
不匹配;这些类型必须是完全相同。另一个通配符将告诉Java该组合类型也应该是协变的:
List<? extends List<? extends Number>> l = new ArrayList<List<Number>>();
在适当的情况下,您应该绝对使用 ? 类型通配符,不要将其作为一般规则避免使用。例如:
public void doThingWithList(List<List<? extends Number>> list);
允许您传递List<Integer>
或List<Long>
。
public void doThingWithList(List<List<Number>> list);
允许您只传递声明为List<Number>
的参数。是一个微小但功能强大且安全的区别。与表面上看起来的不同,List<Integer>
不是List<Number>
的子类,也不可从中分配。List<Integer>
也不是List<? extends Number
的子类,这就是为什么上面的代码无法编译的原因。
List<? extends Number>
与List<Number>
不是相同类型。前者是后者的超类型。List<? extends Number>
的任何子类型(包括List<Number>
)。List<? extends List<? extends Number>> aList = new ArrayList<List<Number>>();
或者甚至是这样。右侧ArrayList的类型参数与左侧的类型参数相同,因此方差不是一个问题。
List<List<? extends Number>> aList = new ArrayList<List<? extends Number>>();
你应该能够直接说
List<List<Number>> aList = new ArrayList<List<Number>>();
我倾向于尽可能避免使用?
类型通配符。我发现类型注释所带来的开销不值得这样做。
List<List<? extends Number>> aList = new ArrayList<List<? extends Number>>();
aList.add(new ArrayList<Integer>());
List<List<? extends Number>>
给出了编译错误incompatible types: ArrayList<List<Number>> cannot be converted to List<List<? extends Number>>
。这对我来说很反直觉,但是当 RHS 是new ArrayList<List<...>>();
时,不能在 LHS 上使用List<List...>>
。多级通配符会使事情变得棘手。请参见 What is the difference between <? extends Base> and <T extends Base>? 以获取最近相关问题的答案。 - skomisa