在java的泛型中super和extends的區別
環境
java:1.7+
前言
主要講的是<? super T> 和 <? extends T>
的區別!
這個是我在打算封裝一段通用程式碼時,發現經常用的<? extends T>
,網上搜索時,發現其總是要和<? super T>
進行比較。
從stackoverflow
中看到一個很好的解釋,這裡記下筆記;
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 T
,例如:List<? 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());
}
}
}