原文連結 李璟([email protected])
經常發現有list<? super t>、set<? extends t>的聲明,是什麼意思呢?<? super t>表示包括t在内的任何t的父類,<? extends t>表示包括t在内的任何t的子類,下面我們詳細分析一下兩種通配符具體的差別。
list<? extends number> foo3的通配符聲明,意味着以下的指派是合法的:
讀取操作通過以上給定的指派語句,你一定能從foo3清單中讀取到的元素的類型是什麼呢?你可以讀取到number,因為以上的清單要麼包含number元素,要麼包含number的類元素。
你不能保證讀取到integer,因為foo3可能指向的是list<double>。
你不能保證讀取到double,因為foo3可能指向的是list<integer>。
寫入操作過以上給定的指派語句,你能把一個什麼類型的元素合法地插入到foo3中呢?
你不能插入一個integer元素,因為foo3可能指向list<double>。
你不能插入一個double元素,因為foo3可能指向list<integer>。
你不能插入一個number元素,因為foo3可能指向list<integer>。
你不能往list<? extends t>中插入任何類型的對象,因為你不能保證清單實際指向的類型是什麼,你并不能保證清單中實際存儲什麼類型的對象。唯一可以保證的是,你可以從中讀取到t或者t的子類。
現在考慮一下list<? super t>。
list<? super integer> foo3的通配符聲明,意味着以下指派是合法的:
讀取操作通過以上給定的指派語句,你一定能從foo3清單中讀取到的元素的類型是什麼呢?你不能保證讀取到integer,因為foo3可能指向list<number>或者list<object>。
你不能保證讀取到number,因為foo3可能指向list<object>。
唯一可以保證的是,你可以讀取到object或者object子類的對象(你并不知道具體的子類是什麼)。
寫入操作通過以上給定的指派語句,你能把一個什麼類型的元素合法地插入到foo3中呢?你可以插入integer對象,因為上述聲明的清單都支援integer。
你可以插入integer的子類的對象,因為integer的子類同時也是integer,原因同上。
你不能插入double對象,因為foo3可能指向arraylist<integer>。
你不能插入number對象,因為foo3可能指向arraylist<integer>。
你不能插入object對象,因為foo3可能指向arraylist<integer>。
請記住pecs原則:生産者(producer)使用extends,消費者(consumer)使用super。
生産者使用extends
如果你需要一個清單提供t類型的元素(即你想從清單中讀取t類型的元素),你需要把這個清單聲明成<? extends t>,比如list<? extends integer>,是以你不能往該清單中添加任何元素。
消費者使用super
如果需要一個清單使用t類型的元素(即你想把t類型的元素加入到清單中),你需要把這個清單聲明成<? super t>,比如list<? super integer>,是以你不能保證從中讀取到的元素的類型。
即是生産者,也是消費者
如果一個清單即要生産,又要消費,你不能使用泛型通配符聲明清單,比如list<integer>。
請參考java.util.collections裡的copy方法(jdk1.7):
我們可以從java開發團隊的代碼中獲得到一些啟發,copy方法中使用到了pecs原則,實作了對參數的保護。