簡單來說,通過List<? extends T>定義的list只能從裏面取數據,取出來的對象都是T類型或者T類型的子類。
對於List<? super T>定義的list,只能將對象元素放入list,放入的對象是T類型或者T類型的子類。
總結爲PECS原則:由於<? extends T>的只能取,不能存,而<? super T>得只能存,不能取,因此就有producer用<? extends T>,consumer用<? super T>。
比如有3個類的繼承關係如下:
public class MySuperObject {
public void superMethod(){
System.out.println("super method " +this);
}
}
public class MyObject extends MySuperObject{
public void myMethod(){
System.out.println("myMethod " + this);
}
}
public class MyChildObject extends MyObject{
public void childMethod(){
System.out.println("childMethod " + this);
}
}
對應List<? extends T>使用場景爲:
public class TestExtends {
public static void testObjExtendsList(List<? extends MyObject> objs){
//? extends MyObject 不能添加
//numbers.add(new MyObject());
//因爲對象類型一定是MyObject或者MyObject的子類,所以可以調用MyObject或者MyObject父類的方法
objs.forEach( obj -> {
obj.superMethod();
obj.myMethod();
});
}
public static void main(String[] args){
List<MyObject> objs = new ArrayList<>();
objs.add(new MyObject());
objs.add(new MyChildObject());
testObjExtendsList(objs);
List<MyChildObject> childObjs = new ArrayList<>();
childObjs.add(new MyChildObject());
childObjs.add(new MyChildObject());
testObjExtendsList(childObjs);
}
}
List<? super T>使用場景:
public class TestSuper {
public static void testSuperList(List<? super MyObject> objs){
//只要是MyObject或者MyObject的子類都可以放進List
objs.add(new MyObject());
objs.add(new MyChildObject());
//#1 不能放MyObject的父類
//objs.add(new MySuperObject());
//#2 不能從List取元素,取出來的元素類型不確定
//objs.forEach( obj -> System.out.println(obj));
}
public static void main(String[] args){
//List<? super MyObject> 表示裏面的元素可以當做MyObject或者MyObject的父類來使用,
//MyObject或者MyObject的子類都是可以轉型爲MyObject或者MyObject的父類
List<MyObject> objs = new ArrayList<>();
testSuperList(objs);
objs.forEach( obj -> {
obj.superMethod();
obj.myMethod();
});
List<MySuperObject> superObjs = new ArrayList<>();
testSuperList(superObjs);
superObjs.forEach( obj -> {
obj.superMethod();
//obj.myMethod();
});
}
}
如何理解producer用<? extends T>,consumer用<? super T>,如上所示:
testObjExtendsList方法的調用者爲producer,因爲需要準備數據(放入數據到list)。
testSuperList方法的調用者爲consumer,因爲是數據使用者(從list取數據)。
JDK底層源碼用到的地方:
java.util.Collections.copy(List<? super T> dest, List<? extends T> src)
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());
}
}
}
......
}