为什么我们不能做java:
List<List<? extends Number>> aList = new ArrayList<List<Number>>();
即使这样也可以:
List<? extends Number> aList = new ArrayList<Number>();
编译器错误消息是:
Type mismatch: cannot convert from ArrayList<List<Number>> to List<List<? extends Number>>
答案 0 :(得分:73)
在Java中,如果Car
是Vehicle
的派生类,那么我们可以将所有Cars
视为Vehicles
; Car
是Vehicle
。 但是,List
的{{1}}也不是Cars
List
。我们说Vehicles
与<{1}} 不协变。
Java要求您明确告诉它何时使用由List<Car>
标记表示的通配符的协方差和逆变。看看你的问题发生在哪里:
List<Vehicle>
内部?
有效,因为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.
不匹配;类型必须完全相同。另一个通配符将告诉Java这个组合类型也应该是协变的:
List<? extends Number>
答案 1 :(得分:3)
我对Java语法不是很熟悉,但似乎你的问题是:
答案 2 :(得分:3)
你一定要使用?在适当的时候键入通配符,不要将其作为一般规则来避免。例如:
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>
的子类,也不是可分配的。{1}}。 List<Integer>
也不是List<? extends Number
的子类,这就是上面代码无法编译的原因。
答案 3 :(得分:2)
您的语句未编译,因为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>>();
我倾向于尽可能避免使用?
类型的通配符。我发现类型注释中产生的费用不值得获益。
答案 4 :(得分:-2)
List<List<? extends Number>> aList = new ArrayList<List<? extends Number>>();
aList.add(new ArrayList<Integer>());