如何添加到List <! - ?扩展数字 - >数据结构?

时间:2010-05-05 21:38:00

标签: java generics

我有一个List,声明如下:

 List<? extends Number> foo3 = new ArrayList<Integer>();

我试图将3添加到foo3。但是我收到如下错误消息:

The method add(capture#1-of ? extends Number) in the type List<capture#1-of ?
extends Number> is not applicable for the arguments (ExtendsNumber)

8 个答案:

答案 0 :(得分:275)

抱歉,但你不能。

List<? extends Number> foo3的通配符声明意味着变量foo3可以保存一系列类型中的任何值(而不是特定类型的任何值)。这意味着其中任何一项都是合法的任务:

List<? extends Number> foo3 = new ArrayList<Number>;  // Number "extends" Number
List<? extends Number> foo3 = new ArrayList<Integer>; // Integer extends Number
List<? extends Number> foo3 = new ArrayList<Double>;  // Double extends Number

因此,考虑到这一点,您可以添加哪种类型的对象List foo3,这些对象在上述任何ArrayList任务之后都是合法的:

  • 您无法添加Integer,因为foo3可能指向List<Double>
  • 您无法添加Double,因为foo3可能指向List<Integer>
  • 您无法添加Number,因为foo3可能指向List<Integer>

您无法向List<? extends T>添加任何对象,因为您无法保证它真正指向的是哪种List,因此您无法保证该对象被允许在那List。唯一的“保证”是您只能从中读取,并且您将获得T的{​​{1}}或子类。

反向逻辑适用于T,例如super。这些是合法的:

List<? super T>

您无法从List<? super Number> foo3 = new ArrayList<Number>; // Number is a "super" of Number List<? super Number> foo3 = new ArrayList<Object>; // Object is a "super" of Number 读取特定类型T(例如Number),因为您无法保证它真正指向的List<? super T>类型。您唯一的“保证”是,您可以添加List类型的值(或T的任何子类),而不会违反所指向列表的完整性。


完美的例子是T的签名:

Collections.copy()

注意public static <T> void copy(List<? super T> dest,List<? extends T> src) 列表声明如何使用src允许我从一系列相关List类型中传递任何List,并且仍然保证它将生成T类型或T的子类的值。但是你无法添加到extends列表。

src列表声明使用dest允许我从一系列相关List类型中传递任何List,并且仍然保证我可以将特定类型T的值写入该列表。但如果从列表中读取,则无法保证读取特定类型T的值。

现在,感谢泛型通配符,我可以使用这种方法进行任何调用:

super

考虑这个令人困惑且非常宽泛的代码来锻炼你的大脑。注释掉的行是非法的,其原因是在行的最右侧(需要滚动查看其中一些):

// copy(dest, src)
Collections.copy(new ArrayList<Number>(), new ArrayList<Number());
Collections.copy(new ArrayList<Number>(), new ArrayList<Integer());
Collections.copy(new ArrayList<Object>(), new ArrayList<Number>());
Collections.copy(new ArrayList<Object>(), new ArrayList<Double());

答案 1 :(得分:14)

你不能(没有不安全的演员表)。你只能从中读取。

问题是你不知道列表的确切列表是什么。它可以是Number的任何子类的列表,因此当您尝试将元素放入其中时,您不知道该元素实际上适合列表。

例如,列表可能是Byte的列表,因此将Float放入其中会出错。

答案 2 :(得分:2)

你可以这样做:

  List<Number> foo3 = new ArrayList<Number>();      
  foo3.add(3);

答案 3 :(得分:1)

即使我在这里阅读了答案,也一直让我感到困惑,直到我发现了帕维尔·米纳耶夫(Pavel Minaev)的评论:

  

请注意列表<?扩展Number>并不意味着“对象列表   不同的类型,所有这些类型都以Number扩展。”表示“   扩展Number的单一类型的对象”

在此之后,我能够理解BertF的出色解释。列表<?扩展Number>是什么意思?可以是扩展Number(Integer,Double等)的任何类型,并且声明中的未清除(列表<?extended Number> list)是哪种类型,因此当您想使用add方法时不知道输入是否具有相同的类型;到底是什么类型?

那么List <的元素扩展Number>只能在构造时设置。

还要注意这一点:当我们使用模板时,我们告诉编译器我们弄乱了什么类型。例如, T 会为我们保留该类型,但不会一样

我得说..这是一个要解释/学习的肮脏的东西之一

答案 4 :(得分:0)

&#34;列出&#39;&lt;&#39; ? extends Number&gt;实际上是上限通配符!

上限通配符表示任何扩展Number或Number本身的类都可以用作形式参数类型: 问题源于 Java不知道List 究竟是什么类型的事实。 它必须是一个精确和独特的类型。我希望它有所帮助:)

答案 5 :(得分:0)

您可以通过创建对列表的其他类型的引用来捏造它。

(这些是sepp2k提到的“不安全的强制转换”。)

List<? extends Number> list = new ArrayList<Integer>();

// This will not compile
//list.add(100);

// WORKS, BUT NOT IDEAL
List untypedList = (List)list;
// It will let you add a number
untypedList.add(200);
// But it will also let you add a String!  BAD!
untypedList.add("foo");

// YOU PROBABLY WANT THIS
// This is safer, because it will (partially) check the type of anything you add
List<Number> superclassedList = (List<Number>)(List<?>)list;
// It will let you add an integer
superclassedList.add(200);
// It won't let you add a String
//superclassedList.add("foo");
// But it will let you add a Float, which isn't really correct
superclassedList.add(3.141);
// ********************
// So you are responsible for ensuring you only add/set Integers when you have
// been given an ArrayList<Integer>
// ********************

// EVEN BETTER
// If you can, if you know the type, then use List<Integer> instead of List<Number>
List<Integer> trulyclassedList = (List<Integer>)(List<?>)list;
// That will prevent you from adding a Float
//trulyclassedList.add(3.141);

System.out.println("list: " + list);

由于untypedListsuperclassedListtrulyclassedList只是对list的引用,因此您仍将元素添加到原始ArrayList中。

在上面的示例中,您实际上并不需要使用(List<?>),但是您可能需要在代码中使用它,具体取决于获得的list的类型。

请注意,使用?会向您发出编译器警告,直到将其放在函数上方:

@SuppressWarnings("unchecked")

答案 6 :(得分:0)

因为3是primitive (int),没有扩展Number,所以可以加上它的装箱类型(Integer.valueOf(3))。虽然在IDEA中显示为错误,但仍然可以正常执行。

答案 7 :(得分:-1)

从'Object'扩展列表的地方,可以使用list.add;当要使用list.get时,只需将Object强制转换为对象;