花式仿制药捕获碰撞

时间:2011-05-02 13:11:52

标签: java generics

请告诉我这里发生了什么:

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),或者坚持使用类型转换抑制注释(通常会使代码稍微变得麻烦和复杂化)?

5 个答案:

答案 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(生产者延伸,消费者超级)

  • 您不能将任何内容放入使用EXTENDS通配符声明的类型中 除了值null,它属于每个引用类型
  • 您无法从使用SUPER声明的类型中获取任何内容 通配符,但Object类型的值除外,它是一个超类型 每种参考类型

答案 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是否相同?