1. 程式人生 > >Java泛型的逆變

Java泛型的逆變

實驗準備

在上篇《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>。