环境
java:1.7+
前言
主要讲的是<? super T> 和 <? extends T>
的区别!
这个是我在打算封装一段通用代码时,发现经常用的<? extends T>
,网上搜索时,发现其总是要和<? super T>
进行比较。
从*
中看到一个很好的解释,这里记下笔记;
extends
声明通配符List<? extends Number> foo3
,其意味着有如下的可能:
// Number "extends" Number (in this context)
List<? extends Number> foo3 = new ArrayList<Number>();
// Integer extends Number
List<? extends Number> foo3 = new ArrayList<Integer>();
// Double extends Number
List<? extends Number> foo3 = new ArrayList<Double>();
读取或者获取:
考虑到上面的情况,你可以确定在List foo3
中读取到的类型是:
- 你可以读取到你可以读取到
Number
类型的数字;因为列表都是包含Number
或者Number
子类的。 - 你不能读取到
Integer
,因为foo3
可能会指向一个List<Double>
. - 你不能读取到
Double
,因为foo3
可能会指向一个List<Integer>
.
写入:
考虑上面的情况,添加什么样的类型使得foo3
上面都是合法的。
- 你不能添加
Integer
,因为foo3
可能是List<Double>
. - 你不能添加
Double
,因为foo3
可能是List<Integer>
. - 你不能添加
Number
,因为foo3
可能是List<Integer>
.
也就是说,你不能添加任何对象到List<? extends T>
,因为你不能确定List
真正指向的是哪个,所以你不能保证该列表中允许哪个对象。你能确定的只有,你能从中得到T
或者T
的子类。
super
现在讨论下:List <? super T>
声明通配符List<? super Integer> foo3
,其意味着如下的可能:
// Integer is a "superclass" of Integer (in this context)
List<? super Integer> foo3 = new ArrayList<Integer>();
// Number is a superclass of Integer
List<? super Integer> foo3 = new ArrayList<Number>();
// Object is a superclass of Integer
List<? super Integer> foo3 = new ArrayList<Object>();
读取
基于上面的情况,当从List foo3
中读取时,你能确定收到什么样的类型对象:
- 你不能保证得到
Integer
,因为foo3
可能指向一个List<Number>
或者List<Object>
。 - 你不能保证得到
Number
,因为foo3
可能指向一个一个List<Object>
。 - 你只能保证得到,你将会得到一个
Object
或者Object
子类(但是你不知道具体什么样的子类)。
写入
基于上面的情况,你可以添加什么类型的对象到List foo3
中来满足上面的情况:
- 你可以添加
Integer
,因为Integer
满足上面的所有的情况 - 你可以添加
Integer
的子类实例,因为该实例也满足上面的情况。 - 你不能添加
Double
,因为foo3
可能指向ArrayList<Integer>
. - 你不能添加
Number
,因为foo3
可能指向ArrayList<Integer>
。 - 你不能添加
Object
,因为foo3
可能指向ArrayList<Integer>
。
PECS
PECS
是Producer Extends, Consumer Super
的缩写 :
Producer Extends 如果你需要一个
List
来生产T
值(你想从list
中读取T
),你需要将其声明为?
,例如:
extends TList<? extends Integer>
。但是你不能使用add
方法。Consumer Super: 如果你需要一个
List
去消费T
值(你想写入T
到List
),你需要将其声明为? super T
,例如:List<? super Integer>
。但是你不能确定从list
中读取到的是什么类型对象。- 如果你即想从
list
中读取又想去写入,那么你需要声明为一个具体的泛型,而不是通配符。
例如:List<Integer>
.
下面是同时使用extends
和super
的情况。
public class Collections {
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
for (int i = 0; i < src.size(); i++)
dest.set(i, src.get(i));
}
}
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
int srcSize = src.size();
if (srcSize > dest.size())
throw new IndexOutOfBoundsException("Source does not fit in dest");
if (srcSize < COPY_THRESHOLD ||
(src instanceof RandomAccess && dest instanceof RandomAccess)) {
for (int i=0; i<srcSize; i++)
dest.set(i, src.get(i));
} else {
ListIterator<? super T> di=dest.listIterator();
ListIterator<? extends T> si=src.listIterator();
for (int i=0; i<srcSize; i++) {
di.next();
di.set(si.next());
}
}
}