在上篇《java泛型——協變》這篇文章中遺留以下問題——協變不能解決將子類型添加到父類型的泛型列表中。本篇將用逆變來解決這個問題。
我們首先增加以下方法,見代碼清單1所示。
代碼清單1
/** * * 描 述:Exp3使用逆變<br/> * 作 者:jiaan.gja<br/> * 歷 史: (版本) 作者 時間 注釋 <br/> * @param itemList */ public void doDecorate3(List<? super T> itemList, T t) { for(int i = 0; i < itemList.size(); i++) { System.out.PRintln(itemList.get(i)); } } /** * * 描 述:Exp3使用逆變<br/> * 作 者:jiaan.gja<br/> * 歷 史: (版本) 作者 時間 注釋 <br/> * @param itemList */ public void addDecorate3(List<? super T> itemList, T t) { itemList.add(t); }
語法List<? super T>即為Java泛型的逆變,addDecorate3方法中的itemList.add(t)語句也沒有協變出現的編譯問題。
現在我們嘗試下逆變的用途,如代碼清單2所示。
代碼清單2
/** * * 類 名: Exp3<br/> * 描 述: 泛型的逆變使用<br/> * 作 者: 耿嘉安<br/> * 創 建: 2015年8月25日<br/> * * 歷 史: (版本) 作者 時間 注釋 */class Exp3 { 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.addDecorate3(listA, auctionOne); decoratorA.addDecorate3(listA, auctionTwo); decoratorA.doDecorate3(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.addDecorate3(listB, tableOne); decoratorB.addDecorate3(listB, tableTwo); decoratorB.doDecorate3(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.addDecorate3(listC, serviceOne); decoratorC.addDecorate3(listC, serviceTwo); decoratorC.doDecorate3(listC); /** * 測試逆變開始 */ decoratorA.doDecorate3(listB); decoratorA.doDecorate3(listC); }}
我們發現以下兩條語句都會編譯出錯:
decoratorA.doDecorate3(listB); decoratorA.doDecorate3(listC);
我們暫且將這個問題放一放,將它們暫時注釋掉,增加代碼清單3中的代碼。
代碼清單3
List<Object> listD = new ArrayList<Object>(); decoratorA.doDecorate3(listD); decoratorA.doDecorate3(listA); decoratorA.doDecorate3(listB); decoratorA.doDecorate3(listC); decoratorB.doDecorate3(listD); decoratorB.doDecorate3(listA); decoratorB.doDecorate3(listB); decoratorB.doDecorate3(listC); decoratorC.doDecorate3(listD); decoratorC.doDecorate3(listA); decoratorC.doDecorate3(listC); decoratorC.doDecorate3(listB);
代碼清單3中,decoratorA.doDecorate3(listB);decoratorA.doDecorate3(listC);decoratorB.doDecorate3(listC);decoratorC.doDecorate3(listB);這四條語句編譯錯誤,這說明如下聲明是允許的:
List<? super Auction> itemList = new ArrayList<Object>();List<? super Auction> itemList = new ArrayList<Auction>();List<? super Table> itemList = new ArrayList<Object>();List<? super Table> itemList = new ArrayList<Auction>();List<? super Table> itemList = new ArrayList<Table>();List<? super Service> itemList = new ArrayList<Object>();List<? super Service> itemList = new ArrayList<Auction>();List<? super Service> itemList = new ArrayList<Service>();
而下面這樣是不允許的:
List<? super Auction> itemList = new ArrayList<Table>(); List<? super Auction> itemList = new ArrayList<Service>(); List<? super Table> itemList = new ArrayList<Service>(); List<? super Service> itemList = new ArrayList<Table>();
現在我們編寫以下代碼:
List<? super Auction> itemList = listA; Object o = itemList.get(0); Auction a = itemList.get(0); Table t = itemList.get(0); Service s = itemList.get(0); itemList = listD; o = itemList.get(0); a = itemList.get(0); t = itemList.get(0); s = itemList.get(0);
上述代碼中,除了從itemList獲取Object的語句之外,都會編譯出錯,這是為什么?因為itemList有可能是一個ArrayList<Object>,所以轉型為Auction是可能錯誤的。itemList可能是ArrayList<Object>或者ArrayList<Auction>,所以轉型為Table或者Service必然是不對的。
最后我們增加以下代碼:
itemList.add(new Auction("auctionThr")); itemList.add(new Service("serviceThr", "methodThr")); itemList.add(new Table("tableThr", 10));
上述代碼沒有問題,因為不論itemList是ArrayList<Object>或者ArrayList<Auction>,向其中添加Auction、Table、Service都符合最初的原則。
假如向itemList添加Object是編譯失敗的,因為itemList可能是ArrayList<Auction>。
如果向泛型中添加子類,可以使用逆變來實現。如果要從泛型中獲取子類,逆變有轉型錯誤,此時應該使用協變。
新聞熱點
疑難解答