Java 泛型(通配符)

关于 Java 中的通配符,我有几个问题:

  1. List<? extends T>List<? super T>有什么不同?

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

76775 次浏览

在第一个问题中,<? extends T><? super T>是有界通配符的例子。无界通配符看起来像 <?>,基本上意味着 <? extends Object>。它大致意味着泛型可以是任何类型。有界通配符(<? extends T><? super T>)对类型施加限制,它要么必须是特定类型的 延伸(<? extends T>被称为上界) ,要么必须是特定类型的祖先(<? super T>被称为下界)。

The Java Tutorials have some pretty good explanations of generics in the articles 通配符 and 通配符更有趣.

如果您有一个类层次结构 A,那么 BA的子类,而 CD都是 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 B,其中 B是某种类型。也就是说,类型是未知的,但是可以在其上放置“绑定”。在这种情况下,它被某个类所界定,这个类是 B 的一个子类。

Josh Bloch 也有一个很好的解释,在这个 谷歌视频聊天中什么时候使用 superextends,他提到了 生产者 ABC1消费者 super助记符。

演讲幻灯片:

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

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

– src is an E producer

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

– dst is an E consumer

There may be times when you'll want to restrict the kinds of types that are allowed to be passed to a type parameter. For example, a method that operates on numbers might only want to accept instances of Number or its subclasses. This is what bounded type parameters are for.

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 超类的任何对象)。

一般来说,

如果一个结构包含形式为 ? extends E的元素,我们可以从结构中获取元素,但是不能将 elements into the structure

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]");

To put elements into the structure we need another kind of wildcard called 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));
}
}

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

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

  1. 如果这个方法只从 List<A>读取对象,那么我们应该允许给这个方法 List<A-sub>。(因为 A-sub 是 A)
  2. If this method only inserts objects to List<A>, then we should be allowed to give List<A-super> to this method. (Because A IS a A-super)

以身作则:

consider the sort() method in Collections class which use both extends and super:

    public static <T extends Comparable<? super T>> void sort(List<T> list){...}

所以

为什么是 <T extends Comparable<...>>: ,因为我们 需要列出的项(T)是 Comparable接口的一个子类。

why Comparable<? super T>: becuase we 允许 the Comparable type to be a 任何超类型 T 的可比性.

考虑一下

interface Comparable<T>{
public int compareTo(T o);
}


public static <T extends Comparable<? super T>> void sort(List<T> list){...}




public static <T extends Comparable<T>> void sort2(List<T> list){...}




class A implements Comparable<A>{
@Override
public int compareTo(A o) {
...
}
}


class B extends A {
}


List<A> listA = new ArrayList<>();
List<B> listB = new ArrayList<>();


sort(listA);  //ok
sort(listB);  //ok
    

sort2(listA); //ok
sort2(listB); //Error