请告诉我这里发生了什么:
List<? extends Number> a = new ArrayList<Number>();
List<? extends Number> b = new ArrayList<Number>();
a.addAll(b); // ouch! compiler yells at me, see the block below:
/*
incompatible types
found : java.util.List<capture#714 of ? extends java.lang.Number>
required: java.util.List<java.lang.Number>
*/
这个简单的代码无法编译。我依稀记得与类型捕获相关的东西,比如应该主要用于接口规范,而不是实际的代码,但我从来没有像这样傻眼。
这当然可能是强力固定的,就像那样:
List<? extends Number> a = new ArrayList<Number>();
List<? extends Number> b = new ArrayList<Number>();
@SuppressWarnings({"unchecked"})
List<Number> aPlain = (List<Number>) a;
@SuppressWarnings({"unchecked"})
List<Number> bPlain = (List<Number>) b;
aPlain.addAll(bPlain);
那么,我真的是否必须放弃声明中的捕获(捕获来自界面,所以我必须更改一些API),或者坚持使用类型转换抑制注释(通常会使代码稍微变得麻烦和复杂化)?
答案 0 :(得分:9)
您基本上有两个可能不同类型的列表。因为? extends Number
表示扩展Number
的类。因此,对于列表a
,它可以是classA
,对于列表b
,它可以是例如classB
。它们不兼容,它们可能完全不同。
答案 1 :(得分:6)
问题是,如果您使用List<? extends Number>
,您实际上可以这样做:
List<? extends Number> a = new ArrayList<Integer>();
List<? extends Number> b = new ArrayList<Double>();
a.addAll(b); //ouch, would add Doubles to an Integer list
编译器无法从List<? extends Number>
告诉实际的类型参数是什么,因此不允许您进行添加操作。
如果将列表作为参数获取,也不应该将列表转换为List<Number>
,因为您实际上可以拥有一个Integer对象列表并向其添加Double对象。
在这种情况下,您最好创建一个新的List<Number>
并添加两个列表中的对象:
List<Number> c = new ArrayList<Number>(a.size() + b.size());
c.addAll(a);
c.addAll(b);
编辑:如果您在本地创建两个列表,您无论如何都不会找到?
通配符(因为您始终拥有List<Number>
)。
答案 2 :(得分:3)
不要使用?
通配符。这意味着“我不知道某些特定类型”,并且由于您不知道类型,因此无法向列表中添加任何内容。将代码更改为使用List<Number>
,一切正常。
这很快成为最常见的Java问题,在一百种变化......
答案 3 :(得分:2)
PECS(生产者延伸,消费者超级)
答案 4 :(得分:1)
事情是:
List<? extends Number> a = new ArrayList<Number>();
List<? extends Number> b = new ArrayList<Number>();
也可以理解为:
List<x extends Number> a = new ArrayList<Number>();
List<y extends Number> b = new ArrayList<Number>();
编译器应该如何知道x和y是否相同?