Java泛型的逆變
阿新 • • 發佈:2019-02-05
實驗準備
在上篇《Java泛型的協變》這篇文章中遺留以下問題:將子型別新增到父型別的泛型時發現,協變不能解決向泛型列表中新增元素的問題,現在我們增加如下程式碼:
/** * * 描 述:Exp3使用<br/> * 作 者:耿嘉安<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/> * 作 者:耿嘉安<br/> * 歷 史: (版本) 作者 時間 註釋 <br/> * @param itemList */ public void addDecorate3(List<? super T> itemList, T t) { itemList.add(t); }
可以看到我們使用List<? super T>的語法,即逆變聲明瞭doDecorate3和addDecorate3的引數。我們先來看addDecorate3,當呼叫itemList.add(t)語句時已經沒有了編譯錯誤,下面我們看看doDecorate3和addDecorate3的使用。
實驗三:泛型逆變
現在我們嘗試下逆變的用途,程式碼如下:
/** * * 類 名: 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); 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); 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); decoratorA.doDecorate3(listD, new Auction("auctionThr")); decoratorA.doDecorate3(listA, new Auction("auctionThr")); decoratorA.doDecorate3(listB, new Table("tableThr", 10)); decoratorA.doDecorate3(listC, new Service("serviceThr", "methodThr")); decoratorB.doDecorate3(listA, new Table("tableThr", 10)); decoratorB.doDecorate3(listD, new Table("tableThr", 10)); decoratorB.doDecorate3(listB, new Table("tableThr", 10)); decoratorB.doDecorate3(listC, new Table("tableThr", 10)); decoratorC.doDecorate3(listA, new Service("serviceThr", "methodThr")); decoratorC.doDecorate3(listD, new Service("serviceThr", "methodThr")); decoratorC.doDecorate3(listC, new Service("serviceThr", "methodThr")); decoratorC.doDecorate3(listB, new Service("serviceThr", "methodThr")); } }
我們看到addDecorate3編譯正常,但是部分doDecorate3反而編譯出錯了,這說明這樣如下的宣告是允許的:
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 = new ArrayList<Auction>();
Auction auction = itemList.get(0);
Service service = itemList.get(0);
Table table = itemList.get(0);
Object obj = itemList.get(0);
itemList.add(new Auction("auctionThr"));
itemList.add(new Service("serviceThr", "methodThr"));
itemList.add(new Table("tableThr", 10));
我們發現除了能夠從itemList中獲取Object之外,其餘型別都不可以,這是為什麼?因為itemList有可能是一個ArrayList<Object>,所以轉型為Auction是可能錯誤的。itemList可能是ArrayList<Object>或者ArrayList<Auction>,所以轉型為Table或者Service必然是不對的。最後我們看到不論itemList是ArrayList<Object>或者ArrayList<Auction>,向其中新增Auction、Table、Service都符合最初的原則。假如向itemList新增Object是編譯失敗的,因為itemList可能是ArrayList<Auction>。