Java泛型中<? extends E>和<? super E>的區別

這篇文章談一談Java泛型聲明<? extends E>和<? super E>的做用和區別java

<? extends E>

           <? extends E> 是 Upper Bound(上限) 的通配符,用來限制元素的類型的上限,好比安全

  1. List<? extends Fruit> fruits;  

表示集合中的元素類型上限爲Fruit類型,即只能是Fruit或者Fruit的子類,所以對於下面的賦值是合理的app

  1. fruits = new ArrayList<Fruit>();  
  2. fruits = new ArrayList<Apple>();  

若是集合中元素類型爲Fruit的父類則會編譯出錯,好比dom

fruits = new ArrayList<Object>();//編譯不經過  

           有了上面的基礎,接着來看看 <? extends E>限定的集合的讀寫操做ide

一、寫入ui

           由於集合fruits中裝的元素類型爲Fruit或Fruit子類,直覺告訴咱們,往fruits中添加一個Fruit類型對象或其子類對象是可行的spa

  1. fruits.add(new Fruit());//編譯不經過  
  2. fruits.add(new Apple());//編譯不經過  

           結果是編譯都不經過,爲何?由於<? extends Fruit>只是告訴編譯器集合中元素的類型上限,但它具體是什麼類型編譯器是不知道的,fruits能夠指向ArrayList<Fruit>,也能夠指向ArrayList<Apple>、ArrayList<Banana>,也就是說它的類型是不肯定的,既然是不肯定的,爲了類型安全,編譯器只能阻止添加元素了。舉個例子,當你添加一個Apple時,但fruits此時指向ArrayList<Banana>,顯然類型就不兼容了。固然null除外,由於它能夠表示任何類型。.net

 

二、讀取 code

           不管fruits指向什麼,編譯器均可以肯定獲取的元素是Fruit類型,全部讀取集合中的元素是容許的對象

Fruit fruit = fruits.get(0);//編譯經過  

補充:<?>是<? extends Object>的簡寫

<? super E>

      <? super E> 是 Lower Bound(下限) 的通配符 ,用來限制元素的類型下限,好比

List<? super Apple> apples;  

表示集合中元素類型下限爲Apple類型,即只能是Apple或Apple的父類,所以對於下面的賦值是合理的

apples = new ArrayList<Apple>();  
  1. apples = new ArrayList<Fruit>();  
  2. apples = new ArrayList<Object>();  

若是元素類型爲Apple的子類,則編譯不一樣過

apples = new ArrayList<RedApple>();//編譯不經過  

一樣看看<? super E>限定的集合的讀寫操做

一、寫入

        由於apples中裝的元素是Apple或Apple的某個父類,咱們沒法肯定是哪一個具體類型,可是能夠肯定的是Apple和Apple的子類是和這個「不肯定的類」兼容的,由於它確定是這個「不肯定類型」的子類,也就是說咱們能夠往集合中添加Apple或者Apple子類的對象,因此對於下面的添加是容許的

apples.add(new Apple());  
  1. apples.add(new RedApple());  

它沒法添加Fruit的任何父類對象,舉個例子,當你往apples中添加一個Fruit類型對象時,但此時apples指向ArrayList<Apple>,顯然類型就不兼容了,Fruit不是Apple的子類

apples.add(new Fruit());//編譯不經過  

二、讀取

        編譯器容許從apples中獲取元素的,可是沒法肯定的獲取的元素具體是什麼類型,只能肯定必定是Object類型的子類,所以咱們想得到存儲進去的對應類型的元素就只能進行強制類型轉換了

Apple apple = (Apple)apples.get(0);//獲取的元素爲Object類型  

問題來了,JDK1.5引入泛型的目的是爲了不強制類型轉換的繁瑣操做,那麼使用泛型<? super E>幹嗎呢?這裏就得談到泛型PECS法則了

PECS法則

PECS法則:生產者(Producer)使用extends,消費者(Consumer)使用super
一、生產者
       若是你須要一個提供E類型元素的集合,使用泛型通配符<? extends E>。它比如一個生產者,能夠提供數據。
二、消費者
       若是你須要一個只能裝入E類型元素的集合,使用泛型通配符<? super E>。它比如一個消費者,能夠消費你提供的數據。
三、既是生產者也是消費者
       既要存儲又要讀取,那就別使用泛型通配符。

PECS例子

JDK集合操做幫助類Collections中的例子
* 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());  
        }  
    }  
}  

總結

        爲何要引入泛型通配符?一句話:爲了保證類型安全。
 
具體請看 《effective java》裏,Joshua Bloch提出的PECS原則:
  1. 頻繁往外讀取內容的,適合用上界Extends。
  2. 常常往裏插入的,適合用下界Super。
相關文章
相關標籤/搜索