花式仿制药捕获碰撞

Ant*_*n K 6 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>
 */
Run Code Online (Sandbox Code Playgroud)

这个简单的代码无法编译.我依稀记得与类型捕获有关的东西,比如应该主要用于接口规范,而不是实际的代码,但我从来没有像这样傻眼.

这当然可能是强力固定的,就像那样:

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

那么,我是否真的必须放弃声明中的捕获(捕获来自界面,所以我将不得不更改一些API),或坚持使用抑制注释的类型转换(这通常会使代码变得混乱和复杂化一点点)?

Pet*_*hev 9

您基本上有两个可能不同类型的列表.因为? extends Number意味着一个延伸的类Number.因此,对于列表,a它可以是classA列表b,例如classB.它们不兼容,它们可能完全不同.

  • `Java在这方面也有限制:我无法直接创建通配符:new ArrayList <?extends Number>(); "你为什么要那样做?您需要知道的是ArrayList包含Number类型的对象,因此应该使用`new ArrayList <Number>();`.如果你可以使用`?`表示法,你仍然需要检查每个元素的类型.什么会```实际上是什么意思? - "你可以将任何类型为`Number`或子类的对象放入该列表中",这与`new ArrayList <Number>();`完全相同. (2认同)

Tho*_*mas 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
Run Code Online (Sandbox Code Playgroud)

编译器无法判断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);
Run Code Online (Sandbox Code Playgroud)

编辑:如果你在本地创建两个列表,你不会得到?通配符(因为你一直有List<Number>).

  • 删除了我对“解决方法”的评论以防止误解 (2认同)

Mic*_*rdt 5

不要使用?通配符。它的意思是“我不知道某些特定类型”,并且由于您不知道该类型,因此无法将任何内容添加到列表中。更改您要使用的代码List<Number>,一切都会正常。

这正迅速成为最常见的 Java 问题,有一百种变体......