118

我对 Java 中的通用通配符有几个问题:

  1. List<? extends T>和 和有什么不一样List<? super T>

  2. 什么是有界通配符,什么是无界通配符?

4

6 回答 6

130

在您的第一个问题中,<? extends T>并且<? super T>是有界通配符的示例。一个无界通配符看起来像<?>,基本上意味着<? extends Object>。它松散地意味着泛型可以是任何类型。有界通配符(<? extends T><? super T>)通过说它必须扩展特定类型(<? extends T>称为上限)或必须是特定类型的祖先(<? super T>称为下限)来对类型施加限制.

Java 教程在通配符和通配符更有趣的文章中对泛型有一些很好的解释。

于 2008-10-30T23:10:26.287 回答
51

如果你有一个类层次结构 A,B 是 A 的子类,C 和 D 都是 B 的子类,如下所示

class A {}
class B extends A {}
class C extends B {}
class D extends B {}

然后

List<? extends A> la;
la = new ArrayList<B>();
la = new ArrayList<C>();
la = new ArrayList<D>();

List<? super B> lb;
lb = new ArrayList<A>(); //fine
lb = new ArrayList<C>(); //will not compile

public void someMethod(List<? extends B> lb) {
    B b = lb.get(0); // is fine
    lb.add(new C()); //will not compile as we do not know the type of the list, only that it is bounded above by B
}

public void otherMethod(List<? super B> lb) {
    B b = lb.get(0); // will not compile as we do not know whether the list is of type B, it may be a List<A> and only contain instances of A
    lb.add(new B()); // is fine, as we know that it will be a super type of A 
}

有界通配符就像? extends BB 是某种类型。也就是说,类型未知,但可以在其上放置“绑定”。在这种情况下,它受到某个类的限制,该类是 B 的子类。

于 2008-10-30T23:14:24.423 回答
39

Josh Bloch 也很好地解释了何时使用super,并且extends在这个google io 视频谈话中,他提到了Producer extendsConsumersuper助记符。

从演示幻灯片:

假设您要添加批量方法Stack<E>

void pushAll(Collection<? extends E> src);

– src 是 E 生产者

void popAll(Collection<? super E> dst);

– dst 是 E 消费者

于 2008-10-31T08:15:17.187 回答
3

有时您可能想要限制允许传递给类型参数的类型种类。例如,对数字进行操作的方法可能只想接受 Number 或其子类的实例。这就是有界类型参数的用途。

Collection<? extends MyObject> 

意味着它可以接受所有与 MyObject 有IS-A关系的对象(即任何属于 myObject 类型的对象,或者我们可以说是 MyObject 的任何子类的任何对象)或 MyObject 类的对象。

例如:

class MyObject {}

class YourObject extends MyObject{}

class OurObject extends MyObject{}

然后,

Collection<? extends MyObject> myObject; 

将只接受 MyObject 或 MyObject 的子对象(即 OurObject 或 YourObject 或 MyObject 类型的任何对象,但不接受 MyObject 超类的任何对象)。

于 2015-01-08T09:47:05.957 回答
1

一般来说,

如果一个结构包含具有 form 类型的? extends E元素,我们可以从结构中取出元素,但不能将元素放入结构中

List<Integer> ints = new ArrayList<Integer>();
ints.add(1);
ints.add(2);
List<? extends Number> nums = ints;
nums.add(3.14); // compile-time error
assert ints.toString().equals("[1, 2, 3.14]"); 

要将元素放入结构中,我们需要另一种通配符Wildcards with super,称为

 List<Object> objs = Arrays.<Object>asList(2, 3.14, "four");
    List<Integer> ints = Arrays.asList(5, 6);
    Collections.copy(objs, ints);
    assert objs.toString().equals("[5, 6, four]");

    public static <T> void copy(List<? super T> dst, List<? extends T> src) {
          for (int i = 0; i < src.size(); i++) {
                dst.set(i, src.get(i));
         }
    }
于 2015-06-20T16:34:03.990 回答
1

创建通用通配符是为了使在 Collection 上操作的方法更可重用。

例如,如果一个方法有一个参数List<A>,我们只能给List<A>这​​个方法。在某些情况下,这个方法的功能是浪费:</p>

  1. 如果这个方法只从 中读取对象List<A>,那么我们应该被允许给List<A-sub>这个方法。(因为 A-sub 是 A)
  2. 如果这个方法只插入对象到List<A>,那么我们应该被允许给List<A-super>这个方法。(因为 A 是 A-super)
于 2016-12-07T09:26:28.037 回答