在stackoverflow上看到两篇关于java泛型 PECS 的问答:
- Difference between <?super T> and <?extends T> in java
- How can I add to List <?extends Number> data structures
PECS
Remember PECS:"Producer Extends,Consumer Super"
"Producer Extends" - If you need a <code>List</code> to produce <code>T</code> values (you want to read <code>T</code>s from the list), you need to declare it with <code>? extends T</code>, e.g. <code>List<? extends Integer></code>. But you cannot add to this list.
"Consumer Super" - If you need a <code>List</code> to consume <code>T</code> values (you want to write <code>T</code>s into the list), you need to declare it with <code>? super T</code>, e.g. <code>List<? super Integer></code>. But there are no guarantees what type of object you may read from this list.
If you need to both read from and write to a list, you need to declare it exactly with no wildcards, e.g. <code>List<Integer></code>.
也就是说:
- 当你仅仅需要从<code>List</code>中读出<code>T</code>,那么你需要使用<code>? extends T</code>.例如<code>List<? extends Integer></code>
- 当你仅仅需要将T写入<code>List</code>时,那么你需要使用<code>? super T</code>.例如<code>List<? super Integer></code>
- 当你既需要读也需要写时,你应该直接使用T. <code>List< Integer></code>
PECS的使用
extends
<code>List<? extends Number> foo</code> 表示<code>foo</code>可以存储一族的类型的值(而不是一个特殊的类型的值),所以下面的声明都是正确的:
List<? extends Number> foo = new ArrayList<Number>; // Number "extends" Number
List<? extends Number> foo = new ArrayList<Integer>; // Integer extends Number
List<? extends Number> foo = new ArrayList<Double>; // Double extends Number
正因为<code>foo</code>表示存储的是一族的类型的值,无法保证具体指向的类型,所以我们无法保证我们<code>add</code>的对象是<code>List</code>允许的类型。假如我们<code>add</code>一个<code>Integer</code>的值,但是<code>foo</code>可能指向<code>Double</code>(第三条语句),我们<code>add</code>一个<code>Double</code>的值,<code>foo</code>可能指向<code>Integer</code>(第二条语句)。
虽然无法<code>add</code>,但是我们可以保证我们从<code>foo</code>中取出来的值肯定是属于<code>Number</code>或者是<code>Number</code>的子类,所以我们可以从<code>foo</code>中获取值。
super
关于<code>List<? super T></code>,以下声明是正确的:
List<? super Number> foo = new ArrayList<Number>; // Number is a "super" of Number
List<? super Number> foo = new ArrayList<Object>; // Object is a "super" of Number
因为<code>foo</code>的类型无法确定,可能是<code>Number</code>,也可能是<code>Number</code>的父类,我们无法保证读出来的值的类型,所以无法从<code>List</code>中读出值。但是我们可以保证我们插进去的值肯定属于<code>Number</code>或者<code>Number</code>的父类,因此我们可以使用<code>add</code>。
JDK中的例子
<code>Collections.copy()</code>的方法签名:
public static <T> void copy(List<? super T> dest,List<? extends T> src)
在<code>src</code>中,我们可以传入与<code>T</code>类型相关的<code>List</code>,并且能够保证取出的是<code>T</code>类型或者<code>T</code>的子类型的值。
在<code>dest</code>中,我们可以传入<code>T</code>类型和<code>T</code>的父类的<code>List</code>,并且能够保证我们从存放的值都满足要求。
例如:
// copy(dest, src)
Collections.copy(new ArrayList<Number>(), new ArrayList<Number());
Collections.copy(new ArrayList<Number>(), new ArrayList<Integer());
Collections.copy(new ArrayList<Object>(), new ArrayList<Number>());
Collections.copy(new ArrayList<Object>(), new ArrayList<Double());
有兴趣的同学可以去看原文。