泛型类型的Java容器

时间:2011-06-22 11:51:01

标签: java generics

我正在准备考试,教授给我们的一个代码对我来说是模糊的:

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对象添加到此列表中。

4 个答案:

答案 0 :(得分:9)

PECS - 制片人延伸,消费者超级 - 这是布洛赫提出的一个助记符,以应对上述情况。

换句话说,如果您使用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 },它无法进行转换。