泛型类型的Java容器

Mac*_*iej 3 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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

有人可以解释一下这些错误吗?我很确定应该可以将String对象添加到此列表中.

Boz*_*zho 9

PECS - 生产者扩展,消费者超级 - 这是布洛赫提出的一个助记符,以应对上述问题.

换句话说,如果您使用extends,则只能从您的集合中生成元素.如果你想要它消耗元素 - 使用super.

重点是强制编译时安全.如果您的集合是使用定义的? extends SomeBaseClass,那么这将意味着"它可以包含基类的单个子类的实例".在您定义的情况下 new LinkedList<String>(),但是您尝试添加Object- 这不应该被允许,并且它被编译器捕获.


Dor*_*rus 5

在调用时a.add(),java只知道你调用add java.util.LinkedList<? extends Object>.此时链接列表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
Run Code Online (Sandbox Code Playgroud)

请注意,由于a可以包含任何链接列表,因此无法向其添加任何内容.如果你添加一个Object,列表可能是期待的Integer.如果你添加一个Integer,它可能是期待的Checksum.

你写的是同样的问题

static java.util.LinkedList<? extends Number> b = new java.util.LinkedList<Integer>();
Run Code Online (Sandbox Code Playgroud)

然后:

b.add(new Float(10.0f)); // fails
b.add(new Integer(10)); // fails
Run Code Online (Sandbox Code Playgroud)

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

以下代码现在将失败:

static java.util.LinkedList<? super Number> c = new java.util.LinkedList<Integer>(); // fails
Run Code Online (Sandbox Code Playgroud)

因为现在c不能包含List<Integer>,添加一个Float将是有效的.

编辑:返回原始示例.如何在a上使用add函数有一种有点邪恶的方法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
}
Run Code Online (Sandbox Code Playgroud)

注意事项2和3将给出未经检查的投射警告.你冒着堆污染的风险,事实上,案例2确实如此.