你們在平時的工做學習中, 確定會見過很多以下的語句:java
List<? super T> List<? extends T>
咱們都知道, 上面的代碼時關於 Java 泛型的, 那麼這兩個不一樣的寫法都有什麼區別呢?學習
首先, 說到 Java 的泛型, 咱們必需要提到的是Java 泛型的類型擦除機制: Java中的泛型基本上都是在編譯器這個層次來實現的. 在生成的 Java 字節代碼中是不包含泛型中的類型信息的. 使用泛型的時候加上的類型參數, 會被編譯器在編譯的時候去掉. 這個過程就稱爲類型擦除. 如在代碼中定義的List<Object>和List<String>等類型, 在編譯以後都會變成List, JVM看到的只是List, 而由泛型附加的類型信息對JVM來講是不可見的.code
在使用泛型類時, 咱們可使用一個具體的類型, 例如能夠定義一個 List<Integer> 的對象, 咱們的泛型參數就是 Integer; 咱們也可使用通配符 ?
來表示一個未知類型, 例如 List<?> 就表示了泛型參數是某個類型, 只不過咱們並不知道它的具體類型時什麼.
List<?>所聲明的就是全部類型都是能夠的, 但須要注意的是, List<?>並不等同於List<Object>. 對於 List<Object> 來講, 它實際上肯定了 List 中包含的是 Object 及其子類, 咱們可使用 Object 類型來接收它的元素. 相對地, List<?> 則表示其中所包含的元素類型是不肯定, 其中可能包含的是 String, 也多是 Integer. 若是它包含了 String 的話, 往裏面添加 Integer 類型的元素就是錯誤的. 做爲對比, 咱們能夠給一個 List<Object> 添加 String 元素, 也能夠添加 Integer 類型的元素, 由於它們都是 Object 的子類.
正由於類型未知, 咱們就不能經過 new ArrayList<?>()
的方法來建立一個新的ArrayList 對象, 由於編譯器沒法知道具體的類型是什麼. 可是對於 List<?> 中的元素, 咱們卻均可以使用 Object 來接收, 由於雖然類型未知, 但確定是Object及其子類.對象
咱們在上面提到了, List<?> 中的元素只能使用 Object 來引用, 這樣做確定時不太方便的, 不過幸運的是, Java 的泛型機制容許咱們對泛型參數的類型的上界和下界作一些限制, 例如 List<? extends Number> 定義了泛型的上界
是 Number, 即 List 中包含的元素類型是 Number 及其子類. 而 List<? super Number> 定義了泛型的下界
, 即 List 中包含的是 Number 及其父類.
當引入了泛型參數的上界和下界後, 咱們編寫代碼相對來講就方便了許多, 不過也引入了新的問題, 即咱們在何時使用上界, 何時使用下界, 以及它們的區別和限制到底時什麼? 下面我來講說個人理解.get
? extends T 描述了通配符上界, 即具體的泛型參數須要知足條件: 泛型參數必須是 T 類型或它的子類
, 例如:編譯器
List<? extends Number> numberArray = new ArrayList<Number>(); // Number 是 Number 類型的 List<? extends Number> numberArray = new ArrayList<Integer>(); // Integer 是 Number 的子類 List<? extends Number> numberArray = new ArrayList<Double>(); // Double 是 Number 的子類
上面三個操做都是合法的, 由於 ? extends Number 規定了泛型通配符的上界, 即咱們實際上的泛型必需要是 Number 類型或者是它的子類, 而 Number, Integer, Double 顯然都是 Number 的子類(類型相同的也能夠, 即這裏咱們能夠認爲 Number 是 Number 的子類).io
假設有類型 G, 以及 SuperClass 和 SubClass 兩個類, 而且 SuperClass 是 SubClass 的父類, 那麼:編譯
G<? extends SubClass> 是 G<? extends SuperClass> 的子類型. 如 List<? extends Integer> 是 List<? extends Number> 的子類型class
G<SuperClass> 是 G<? extends SuperClass> 的子類型, 例如 List<Integer> 是 List<? extends Integer> 的子類型.泛型
G<?> 和 G<? extends Object> 等同.
能夠想象 G<? extends T> 爲一個左閉右開的區間(T 在最左邊), G<? extends Object> 是最大的區間, 當區間 G<? extends SuperClass> 包含 區間 G<? extends SubClass>時, 那麼較大的區間就是父類.
根據上面的例子, 對於 List<? extends Number> numberArray 對象:
咱們可以從 numberArray 中讀取到 Number 對象, 由於 numberArray 中包含的元素是 Number 類型或 Number 的子類型.
咱們不能從 numberArray 中讀取到 Integer 類型, 由於 numberArray 中可能保存的是 Double 類型.
同理, 咱們也不能從 numberArray 中讀取到 Double 類型.
根據上面的例子, 對於 List<? extends Number> numberArray 對象:
咱們不能添加 Number 到 numberArray 中, 由於 numberArray 有多是List<Double> 類型
咱們不能添加 Integer 到 numberArray 中, 由於 numberArray 有多是 List<Double> 類型
咱們不能添加 Double 到 numberArray 中, 由於 numberArray 有多是 List<Integer> 類型
即, 咱們不能添加任何對象到 List<? extends T> 中, 由於咱們不能肯定一個 List<? extends T> 對象實際的類型是什麼, 所以就不能肯定插入的元素的類型是否和這個 List 匹配. List<? extends T> 惟一能保證的是咱們從這個 list 中讀取的元素必定是一個 T 類型的.
? super T 描述了通配符下界, 即具體的泛型參數須要知足條件: 泛型參數必須是 T 類型或它的父類
, 例如:
// 在這裏, Integer 能夠認爲是 Integer 的 "父類" List<? super Integer> array = new ArrayList<Integer>(); // Number 是 Integer 的 父類 List<? super Integer> array = new ArrayList<Number>(); // Object 是 Integer 的 父類 List<? super Integer> array = new ArrayList<Object>();
對於上面的例子中的 List<? super Integer> array 對象:
咱們不能保證能夠從 array 對象中讀取到 Integer 類型的數據, 由於 array 多是 List<Number> 類型的.
咱們不能保證能夠從 array 對象中讀取到 Number 類型的數據, 由於 array 多是 List<Object> 類型的.
惟一可以保證的是, 咱們能夠從 array 中獲取到一個 Object 對象的實例.
對於上面的例子中的 List<? super Integer> array 對象:
咱們能夠添加 Integer 對象到 array 中, 也能夠添加 Integer 的子類對象到 array 中.
咱們不能添加 Double/Number/Object 等不是 Integer 的子類的對象到 array 中.
有一點須要注意的是, List<? super T>
和 List<? extends T>
中, 咱們所說的 XX 是 T 的父類(a superclass of T)
或 XX 是 T 的子類(a subclass of T)
實際上是針對於泛型參數而言的. 例如考慮以下例子:
List<? super Integer> l1 = ... List<? extends Integer> l2 = ...
那麼這裏 ? super Integer
和 ? extends Integer
的限制是對誰的呢? 是表示咱們能夠插入任意的對象 X 到 l1 中, 只要 X 是 Integer 的父類? 是表示咱們能夠插入任意的對象 Y 到 l2 中, 只要 Y 是 Integer 的子類?
其實不是的, 咱們必需要拋棄上面的概念, ? super Integer
和 ? extends Integer
限制的實際上是 泛型參數
, 即 List<? super Integer> l1
表示 l1
的泛型參數 T 必需要知足 T 是 Integer 的父類
, 所以諸如 List<Object>
, List<Number
的對象就能夠賦值到 l1 中. 正由於咱們知道了 l1 中的泛型參數的邊界信息, 所以咱們就能夠向 l1 中添加 Integer 對象了, 推理過程以下:
令 T 是 l1 的泛型參數, 即: l1 = List<T> = List<? super Integer> 所以有 T 是 Integer 或 Integer 的父類. 若是 T 是 Integer, 則 l1 = List<Integer>, 顯然咱們能夠添加任意的 Integer 對象或 Integer 的子類對象到 l1 中. 若是 T 是 Integer 的父類, 那麼同理, 對於 Integer 或 Integer 的子類的對象, 咱們也能夠添加到 l1 中.
按一樣的分析方式, List<? extends Integer> l2
表示的是 l2 的泛型參數是 Integer 的子類型. 而若是咱們要給一個 List<T> 插入一個元素的話, 咱們須要保證此元素是 T 或是 T 的子類, 而這裏 List<? extends Integer> l2
, l2 的泛型參數是什麼類型咱們都不知道, 進而就不能肯定 l2 的泛型參數的子類是哪些, 所以咱們就不能向 l2 中添加任何的元素了.
來一個對比:
對於 List<? super Integer> l1
:
正確的理解: ? super Integer
限定的是泛型參數. 令 l1 的泛型參數是 T, 則 T 是 Integer 或 Integer 的父類, 所以 Integer 或 Integer 的子類的對象就能夠添加到 l1 中.
錯誤的理解: ? super Integer限定的是插入的元素的類型, 所以只要是 Integer 或 Integer 的父類的對象均可以插入 l1 中
對於 List<? extends Integer> l2
:
正確的理解: ? extends Integer
限定的是泛型參數. 令 l2 的泛型參數是 T, 則 T 是 Integer 或 Integer 的子類, 進而咱們就不能找到一個類 X, 使得 X 是泛型參數 T 的子類, 所以咱們就不能夠向 l2 中添加元素. 不過因爲咱們知道了泛型參數 T 是 Integer 或 Integer 的子類這一點, 所以咱們就能夠從 l2 中讀取到元素(取到的元素類型是 Integer 或 Integer 的子類), 並能夠存放到 Integer 中.
錯誤的理解: ? extends Integer 限定的是插入元素的類型, 所以只要是 Integer 或 Integer 的子類的對象均可以插入 l2 中
PECE 原則: Producer Extends, Consumer Super
Producer extends: 若是咱們須要一個 List 提供類型爲 T 的數據(即但願從 List 中讀取 T 類型的數據), 那麼咱們須要使用 ? extends T, 例如 List<? extends Integer>. 可是咱們不能向這個 List 添加數據.
Consumer Super: 若是咱們須要一個 List 來消費 T 類型的數據(即但願將 T 類型的數據寫入 List 中), 那麼咱們須要使用 ? super T, 例如 List<? super Integer>. 可是這個 List 不能保證從它讀取的數據的類型.
若是咱們既但願讀取, 也但願寫入, 那麼咱們就必須明確地聲明泛型參數的類型, 例如 List<Integer>.
例子:
public class Collections { public static <T> void copy(List<? super T> dest, List<? extends T> src) { for (int i=0; i<src.size(); i++) dest.set(i,src.get(i)); } }
上面的例子是一個拷貝數據的代碼, src 是 List<? extends T> 類型的, 所以它能夠讀取出 T 類型的數據(讀取的數據類型是 T 或是 T 的子類, 可是咱們不能確切的知道它是什麼類型, 惟一能肯定的是讀取的類型 is instance of T), , dest 是 List<? super T> 類型的, 所以它能夠寫入 T 類型或其子類的數據.
Java深度歷險(五)——Java泛型
difference-between-super-t-and-extends-t-in-java