我正在准备考试,教授给我们的一个代码对我来说是模糊的:
public class Z {
static java.util.LinkedList<? extends Object> a =
new java.util.LinkedList<String>();
public static void main(String[] args) {
a.add(null); // 1
a.add(new Object()); // 2
a.add("new Object()"); // 3
System.out.println(a); // 4
}
}
NetBeans在这里给我带来了非常奇怪的编译错误:
no suitable method found for add(java.lang.Object)
method java.util.LinkedList.add(capture#1 of ? extends java.lang.Object) is not applicable
(actual argument java.lang.Object cannot be converted to capture#1 of ? extends java.lang.Object by method invocation conversion)
no suitable method found for add(java.lang.String)
method java.util.LinkedList.add(capture#2 of ? extends java.lang.Object) is not applicable
(actual argument java.lang.String cannot be converted to capture#2 of ? extends java.lang.Object by method invocation conversion)
有人可以解释一下这些错误吗?我很确定应该可以将String对象添加到此列表中。
答案 0 :(得分:9)
换句话说,如果您使用extends
,则只能从您的收藏中生成元素。如果您希望它消耗元素 - 请使用super
。
重点是强制编译时安全。如果您的集合是使用? extends SomeBaseClass
定义的,那么这意味着“它可以包含基类的单个子类的实例”。在您的情况下,您正在定义new LinkedList<String>()
,但是您尝试添加Object
- 这不应该被允许,并且它会被编译器捕获。
答案 1 :(得分:5)
调用a.add()
时,java只知道你在java.util.LinkedList<? extends Object>
上调用add。此时,String
的链接列表未知。您可以在上一行添加另一个链接列表以及任何其他类型。
所有java都知道该列表包含extends Object
的内容。这可能是任何事情,因为java中的每个类都扩展了对象。
a = new java.util.LinkedList<Object>(); // valid
a = new java.util.LinkedList<Integer>(); // valid
a = new java.util.LinkedList<java.util.zip.Checksum>(); // valid
请注意,由于a
可以包含任何链接列表,因此您无法向其添加任何内容。如果您添加Object
,则列表可能会期待Integer
。如果您添加Integer
,则可能需要Checksum
。
你写的问题与
相同static java.util.LinkedList<? extends Number> b = new java.util.LinkedList<Integer>();
然后:
b.add(new Float(10.0f)); // fails
b.add(new Integer(10)); // fails
Java仍然不知道列表是否包含Floats或Integers,因此它禁止这两个。
声明列表的正确方法是:
static java.util.LinkedList<? super Number> c = new java.util.LinkedList<Number>(); // valid
static java.util.LinkedList<? super Number> c = new java.util.LinkedList<Object>(); // valid
以下代码现在将失败:
static java.util.LinkedList<? super Number> c = new java.util.LinkedList<Integer>(); // fails
因为c
现在不能包含List<Integer>
,所以添加Float
将有效。
编辑:
回到最初的例子。如何在List<? extends Object>
public static void main(String[] args) {
a.add(null); // 1
((List<Object>)a).add(new Object()); // 2
((List<Object>)a).add("new Object()"); // 3
System.out.println(a); // 4
}
通知2和3将提供未经检查的投射警告。您冒险heap pollution,事实上,案例2确实如此。
答案 2 :(得分:4)
LinkedList<? extends Object>
的含义是“一个链接列表,只能包含一些扩展Object
的特定未知类的成员”,因此它与{{1}相同}。由于未知哪个类是有问题的类,因此您无法向此类列表添加任何内容。
如果您想拥有一个可以包含任何类成员的列表,请使用LinkedList<?>
。
答案 3 :(得分:2)
通过拥有LinkedList<? extends Object> a
,链接列表不会绑定到类型T
,因为它被通配到扩展对象的 unknown 。
因此,在执行a.add()
时,参数化类型为 unknown ,因此将其翻译为a.add(null)
。它将未知类型键入null
。这意味着您无法向未知类型添加任何内容。
您的//1
可以接受(如上所述),但//2
和//3
不明确。
错误的原因是因为它试图将Method Invocation Conversion设置为未知类型,因为我们不知道extends
Object
unknown },它无法进行转换。