這篇文章談一談Java泛型聲明<? extends E>和<? super E>的作用和區別
<? extends E>
<? extends E> 是 Upper Bound(上限) 的通配符,用來限制元素的類型的上限,比如
- List<? extends Fruit> fruits;
表示集合中的元素類型上限為Fruit類型,即只能是Fruit或者Fruit的子類,因此對於下面的賦值是合理的
- fruits = new ArrayList<Fruit>();
- fruits = new ArrayList<Apple>();
如果集合中元素類型為Fruit的父類則會編譯出錯,比如
有了上面的基礎,接着來看看 <? extends E>限定的集合的讀寫操作
1、寫入
因為集合fruits中裝的元素類型為Fruit或Fruit子類,直覺告訴我們,往fruits中添加一個Fruit類型對象或其子類對象是可行的
- fruits.add(new Fruit());//編譯不通過
- fruits.add(new Apple());//編譯不通過
結果是編譯都不通過,為什么?因為<? extends Fruit>只是告訴編譯器集合中元素的類型上限,但它具體是什么類型編譯器是不知道的,fruits可以指向ArrayList<Fruit>,也可以指向ArrayList<Apple>、ArrayList<Banana>,也就是說它的類型是不確定的,既然是不確定的,為了類型安全,編譯器只能阻止添加元素了。舉個例子,當你添加一個Apple時,但fruits此時指向ArrayList<Banana>,顯然類型就不兼容了。當然null除外,因為它可以表示任何類型。
2、讀取
無論fruits指向什么,編譯器都可以確定獲取的元素是Fruit類型,所有讀取集合中的元素是允許的
補充:<?>是<? extends Object>的簡寫
<? super E>
<? super E> 是 Lower Bound(下限) 的通配符 ,用來限制元素的類型下限,比如
表示集合中元素類型下限為Apple類型,即只能是Apple或Apple的父類,因此對於下面的賦值是合理的
- apples = new ArrayList<Fruit>();
- apples = new ArrayList<Object>();
如果元素類型為Apple的子類,則編譯不同過
同樣看看<? super E>限定的集合的讀寫操作
1、寫入
因為apples中裝的元素是Apple或Apple的某個父類,我們無法確定是哪個具體類型,但是可以確定的是Apple和Apple的子類是和這個“不確定的類”兼容的,因為它肯定是這個“不確定類型”的子類,也就是說我們可以往集合中添加Apple或者Apple子類的對象,所以對於下面的添加是允許的
- apples.add(new RedApple());
它無法添加Fruit的任何父類對象,舉個例子,當你往apples中添加一個Fruit類型對象時,但此時apples指向ArrayList<Apple>,顯然類型就不兼容了,Fruit不是Apple的子類
2、讀取
編譯器允許從apples中獲取元素的,但是無法確定的獲取的元素具體是什么類型,只能確定一定是Object類型的子類,因此我們想獲得存儲進去的對應類型的元素就只能進行強制類型轉換了
問題來了,JDK1.5引入泛型的目的是為了避免強制類型轉換的繁瑣操作,那么使用泛型<? super E>干嘛呢?這里就得談到泛型PECS法則了
PECS法則
PECS例子
* Copies all of the elements from one list into another. After the * operation, the index of each copied element in the destination list * will be identical to its index in the source list. The destination * list must be at least as long as the source list. If it is longer, the * remaining elements in the destination list are unaffected. <p> * * This method runs in linear time. * * @param dest The destination list. * @param src The source list. * @throws IndexOutOfBoundsException if the destination list is too small * to contain the entire source List. * @throws UnsupportedOperationException if the destination list's * list-iterator does not support the <tt>set</tt> operation. */ 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()); } } }
總結
- 頻繁往外讀取內容的,適合用上界Extends。
- 經常往里插入的,適合用下界Super。
