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)
#1 楼
抱歉,您不能这样做。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
中被允许。唯一的“保证”是,您只能从中读取内容,并且会得到T
或super
的子类。相反的逻辑适用于
List<? super T>
,例如Number
。这些是合法的: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
您无法从
List<? super T>
读取特定的类型T(例如List
),因为您不能保证它真正指向的是哪种T
。唯一的“保证”是您能够添加类型T
(或Collections.copy()
的任何子类)的值,而不会破坏所指向列表的完整性。完美的例子是
src
的签名:public static <T> void copy(List<? super T> dest, List<? extends T> src)
请注意
extends
列表声明如何使用src
来允许我传递一组相关List类型的任何List并仍保证它将产生T类型的值或T的子类。但是您不能添加到dest
列表中。super
列表声明使用q4312079q来允许我传递一组相关列表类型中的任何列表,并且仍然保证可以将特定类型T的值写入该列表。但是,如果我从列表中读取数据,就不能保证读取特定类型T的值。因此,现在有了通用通配符,我可以使用单个方法进行任何这些调用:
// 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>());
考虑一下这个令人困惑且范围很广的代码,以锻炼您的大脑。注释掉的行是非法的,并且在行的最右边注明其原因(需要滚动查看其中的某些内容):
List<Number> listNumber_ListNumber = new ArrayList<Number>();
//List<Number> listNumber_ListInteger = new ArrayList<Integer>(); // error - can assign only exactly <Number>
//List<Number> listNumber_ListDouble = new ArrayList<Double>(); // error - can assign only exactly <Number>
List<? extends Number> listExtendsNumber_ListNumber = new ArrayList<Number>();
List<? extends Number> listExtendsNumber_ListInteger = new ArrayList<Integer>();
List<? extends Number> listExtendsNumber_ListDouble = new ArrayList<Double>();
List<? super Number> listSuperNumber_ListNumber = new ArrayList<Number>();
//List<? super Number> listSuperNumber_ListInteger = new ArrayList<Integer>(); // error - Integer is not superclass of Number
//List<? super Number> listSuperNumber_ListDouble = new ArrayList<Double>(); // error - Double is not superclass of Number
//List<Integer> listInteger_ListNumber = new ArrayList<Number>(); // error - can assign only exactly <Integer>
List<Integer> listInteger_ListInteger = new ArrayList<Integer>();
//List<Integer> listInteger_ListDouble = new ArrayList<Double>(); // error - can assign only exactly <Integer>
//List<? extends Integer> listExtendsInteger_ListNumber = new ArrayList<Number>(); // error - Number is not a subclass of Integer
List<? extends Integer> listExtendsInteger_ListInteger = new ArrayList<Integer>();
//List<? extends Integer> listExtendsInteger_ListDouble = new ArrayList<Double>(); // error - Double is not a subclass of Integer
List<? super Integer> listSuperInteger_ListNumber = new ArrayList<Number>();
List<? super Integer> listSuperInteger_ListInteger = new ArrayList<Integer>();
//List<? super Integer> listSuperInteger_ListDouble = new ArrayList<Double>(); // error - Double is not a superclass of Integer
listNumber_ListNumber.add(3); // ok - allowed to add Integer to exactly List<Number>
// These next 3 are compile errors for the same reason:
// You don't know what kind of List<T> is really
// being referenced - it may not be able to hold an Integer.
// You can't add anything (not Object, Number, Integer,
// nor Double) to List<? extends Number>
//listExtendsNumber_ListNumber.add(3); // error - can't add Integer to *possible* List<Double>, even though it is really List<Number>
//listExtendsNumber_ListInteger.add(3); // error - can't add Integer to *possible* List<Double>, even though it is really List<Integer>
//listExtendsNumber_ListDouble.add(3); // error - can't add Integer to *possible* List<Double>, especially since it is really List<Double>
listSuperNumber_ListNumber.add(3); // ok - allowed to add Integer to List<Number> or List<Object>
listInteger_ListInteger.add(3); // ok - allowed to add Integer to exactly List<Integer> (duh)
// This fails for same reason above - you can't
// guarantee what kind of List the var is really
// pointing to
//listExtendsInteger_ListInteger.add(3); // error - can't add Integer to *possible* List<X> that is only allowed to hold X's
listSuperInteger_ListNumber.add(3); // ok - allowed to add Integer to List<Integer>, List<Number>, or List<Object>
listSuperInteger_ListInteger.add(3); // ok - allowed to add Integer to List<Integer>, List<Number>, or List<Object>
评论
为什么允许我们写`List <这样的句子?扩展Number> foo3 = new ArrayList
–阿米特(Amit Kumar Gupta)
2011-10-23 9:42
@articlestack:添加不是唯一有用的事情。填充列表后,从列表中读取可能会很有用。通用通配符允许编写通常适用于一系列列表的代码,例如适用于List
#2 楼
您不能(没有不安全的转换)。您只能阅读它们。问题是您不知道列表的确切含义。它可能是Number的任何子类的列表,因此当您尝试将元素放入其中时,您不知道该元素实际上是否适合列表。
例如,列表可能是
Byte
的列表,因此将Float
放入其中将是错误的。#3 楼
“列表'<'?扩展Number>实际上是一个上限通配符!该上限通配符表示,扩展Number或Number本身的任何类都可以用作形式参数类型:
问题源于Java不知道List到底是什么类型的事实。
它必须是EXACT和UNIQUE Type。我希望它会有所帮助:)
#4 楼
即使我在这里阅读了答案,也一直让我感到困惑,直到我找到Pavel Minaev的评论:请注意List <?扩展Number>并不意味着“不同类型的对象列表,所有这些都扩展Number”。它的意思是“扩展了Number的单一类型的
对象的列表”
之后,我就能理解BertF了不起的解释。列表<?扩展Number>是什么意思?可以是扩展Number(Integer,Double等)的任何类型,并且在声明(List <?extended Number> list)中没有明确说明它们是哪一个,因此当您想使用add方法时,如果输入是是否属于同一类型;类型到底是什么?
那么List <的元素呢?扩展Number>只能在构造时设置。
还要注意这一点:当我们使用模板时,我们告诉编译器要弄乱什么类型。例如T对我们来说就是这种类型,但不是吗?也是一样
我得说..这是一个要解释/学习的肮脏的人之一
#5 楼
您可以改为: List<Number> foo3 = new ArrayList<Number>();
foo3.add(3);
#6 楼
您可以通过创建具有不同类型的List的引用来伪造它。(这些是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);
因为
untypedList
,superclassedList
和trulyclassedList
只是对list
的引用,您仍将元素添加到原始ArrayList中。您实际上不需要在上面的示例中使用
(List<?>)
,但您可能需要在代码中使用它,具体取决于所给的list
的类型。请注意,使用
?
会向您提供编译器警告,直到将其置于函数上方: @SuppressWarnings("unchecked")
评论
注意List <?扩展Number>并不表示“具有不同扩展名的所有不同类型的对象的列表”。它的意思是“扩展了Number的单一类型的对象列表”。您最好检查PECS规则,生产者扩展,消费者超级。 stackoverflow.com/questions/2723397 / ...