Java泛型(通配符)


Answers:


123

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

Java教程在“ 通配符”和“ 通配符的更多乐趣 ”一文中对泛型有很好的解释。


只是为了正确,如果A <B和B <C那么:<A扩展C>是错误的吗?
巴勃罗·费尔南德斯

如果用A <B表示A扩展了B,则A扩展了C。但是您不会在通配符语法中使用它,而是说<?扩展C>限制你的选择,无论是A或B.
比尔的蜥蜴

在这种情况下,如果我说<?超级C>有什么区别?
Pablo Fernandez,

3
只是想推荐有关Java泛型的另一参考:angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html
Zach Scrivena,

3
@Pablo:<? super C>这意味着您的类型仅限C于类型层次结构中的上述内容。(对不起,我的回复太晚了。我想我们两年前没有评论通知吗?)
Bill Lizard 2010年

49

如果您具有类层次结构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的子类。


我假设List<? super B>描述为List接受为B类的父类的类型?这就是为什么C和D无法编译hmm的原因?
VolkanGüven19年

38

乔什·布洛赫(Josh Bloch)也对何时使用super以及extends在此google io视频谈话中有很好的解释,他在其中提到了“ 生产者extends消费者”super助记符。

从演示幻灯片:

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

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

– src是电子制作人

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

– dst是电子消费者


我已经阅读了Bloch的书,但是在这种特殊情况下,我仍然看不到extend和super之间的区别。
巴勃罗·费尔南德斯

观看视频,我认为它很清楚。另外,我认为您应该就“ List <?extends T>和List <?super T>之间有什么区别”问另一个问题,希望可以得到更多答案。(如果您这样做,请从此处添加链接)
空白

2
床-为什么不在此答案中包含示例以使其完整且独立。链接是短暂的。
James Schek

3

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

Collection<? extends MyObject> 

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

例如:

class MyObject {}

class YourObject extends MyObject{}

class OurObject extends MyObject{}

然后,

Collection<? extends MyObject> myObject; 

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


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));
         }
    }

1

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

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

  1. 如果此方法仅从中读取对象List<A>,则应允许我们使用List<A-sub>此方法。(因为A-sub是A A)
  2. 如果此方法仅将对象插入List<A>,则应允许我们使用List<A-super>此方法。(因为A是A超级)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.