JAVA泛型——協變

 

  在上篇《JAVA泛型——基本使用》這篇文章中遺留如下問題,即將子類型Table或者也能添加到父類型Auction的泛型中,要實現這種功能必須藉助於協變。html

實驗準備

  如今在《JAVA泛型——基本使用》一文展現的Decorator類型的基礎上,增長一些代碼,如代碼清單1所示。post

代碼清單1測試

複製代碼
/**
     * 
     * 描 述:Exp2使用br/>
     * 做 者:jiaan.gja<br/>
     * 歷 史: (版本) 做者 時間 註釋 <br/>
     * @param itemList
     */
    public void doDecorate2(List<? extends T> itemList) {
        for(int i = 0; i < itemList.size(); i++) {
            System.out.println(itemList.get(i));
        }
    }
    
    /**
     * 
     * 描 述:Exp2使用<br/>
     * 做 者:jiaan.gja<br/>
     * 歷 史: (版本) 做者 時間 註釋 <br/>
     * @param itemList
     * @param t
     */
    public void addDecorate2(List<? extends T> itemList, T t) {
    // itemList.add(t);
    }
複製代碼

  能夠看到這跟《JAVA泛型——基本使用》中的doDecorate和addDecorate方法幾乎同樣,惟一的區別是將List<T>改成List<? extends T>,這種語法被稱爲協變。若是你已經在IDE裏寫了上面的代碼,確定發現addDecorate2中的語句itemList.add(t);已經編譯報錯。因此咱們先將itemList.add(t);這條語句暫時註釋掉,先來看看doDecorate2有沒有達到咱們的預期。url

實驗:泛型協變

  如今咱們看看協變的用途,即調用doDecorate2方法,如代碼清單2所示。spa

代碼清單2code

複製代碼
/**
 * 
 * 類 名: Exp2<br/>
 * 描 述: 泛型的協變使用<br/>
 * 做 者: jiaan.gja<br/>
 * 創 建: 2015年8月20日<br/>
 *
 * 歷 史: (版本) 做者 時間 註釋
 */
class Exp2 {
    public static void main(String[] args) {

        Decorator<Auction> decoratorA = new Decorator<Auction>();
        List<Auction> listA = new ArrayList<Auction>();
        Auction auctionOne = new Auction("auctionOne");
        Auction auctionTwo = new Auction("auctionTwo");
        decoratorA.addDecorate2(listA, auctionOne);
        decoratorA.addDecorate2(listA, auctionTwo);
        decoratorA.doDecorate2(listA);
        
        Decorator<Table> decoratorB = new Decorator<Table>();
        List<Table> listB = new ArrayList<Table>();
        Table tableOne = new Table("tableOne", 10);
        Table tableTwo = new Table("tableTwo", 20);
        decoratorB.addDecorate2(listB, tableOne);
        decoratorB.addDecorate2(listB, tableTwo);
        decoratorB.doDecorate2(listB);
        
        Decorator<Service> decoratorC = new Decorator<Service>();
        List<Service> listC = new ArrayList<Service>();
        Service serviceOne = new Service("serviceOne", "methodOne");
        Service serviceTwo = new Service("serviceTwo", "methodTwo");
        decoratorC.addDecorate2(listC, serviceOne);
        decoratorC.addDecorate2(listC, serviceTwo);
        decoratorC.doDecorate2(listC);
        
        /*
         * 協變測試開始
         */
        decoratorA.doDecorate2(listB);
        decoratorA.doDecorate2(listC);
        
    }
}
複製代碼

首先,咱們看到doDecorate2方法至少能達到《JAVA泛型——基本使用》文中的doDecorate方法的效果。htm

因爲聲明瞭decoratorA的類型是Decorator<Auction>,那麼此時實例decoratorA的doDecorate2方法的參數能夠認爲是下面這樣:blog

List<? extends Auction> itemList

而listB的類型是ArrayList<Table>,listC的類型是ArrayList<Service>,也就是說協變容許:get

List<? extends Auction> itemList = new ArrayList<Auction>();
List<? extends Auction> itemList = new ArrayList<Table>();
List<? extends Auction> itemList = new ArrayList<Service>();

這說明itemList中既能夠存儲Auction,也能夠存儲Table和Service。咱們作個假設,當你想從itemList中獲取Table的時候,Java編譯器懷疑這實際多是個Service,所以會有編譯錯誤。若是想從itemList中獲取Auction是沒有問題的,由於不管是Table仍是Service它們均可以被自動轉換爲父類Auction。這些推測,能夠經過代碼清單3驗證。編譯器

代碼清單3

複製代碼
        List<? extends Auction> itemList = listA;
        Auction a = itemList.get(0);
        Table t = itemList.get(0);
        Service s = itemList.get(0);
        itemList = listB;
        a = itemList.get(0);
        t = itemList.get(0);
        s = itemList.get(0);
        itemList = listC;
        a = itemList.get(0);
        t = itemList.get(0);
        s = itemList.get(0);
複製代碼

一樣的道理,當你想要向itemList中添加Table時,編譯器懷疑itemList實際是個ArrayList<Service>,所以是不容許的。在獲取Auction時是沒有問題的,那麼添加Auction會不會也能夠?因爲itemList多是ArrayList<Service>或者ArrayList<Table>,根據上一篇《JAVA泛型——基本使用》的結論,這實際也是不能夠的。能夠用下列代碼驗證:

        Auction auctionThree = new Auction("auctionThree");
        itemList.add(auctionThree);
        Auction tableThree = new Auction("tableThree");
        itemList.add(tableThree);
        Auction serviceThree = new Auction("serviceThree");
        itemList.add(serviceThree);

所以這也說明了剛開始看到的addDecorate2中的語句itemList.add(t);爲何會編譯報錯。雖然如此,咱們依然但願能將子類型添加到父類型聲明的泛型中,這該怎麼辦?Java固然也考慮到這個問題,請看下一篇《JAVA泛型——逆變》。

總結

  若是從泛型中獲取子類,應該使用協變。

 

如需轉載,請標明本文做者及出處——做者:jiaan.gja,本文原創首發:博客園,原文連接:http://www.cnblogs.com/jiaan-geng/p/4916347.html 
相關文章
相關標籤/搜索