在學習 Spring 的依賴注入時, 被 Google 導流到了 [Java Generics FAQs](). 這篇文章深刻講解了 Java 中泛型相關的方方面面, 閱讀完畢後, 整理了本身的一些理解.html
在進入具體的討論以前, 咱們須要先明確幾個名詞的含義.java
interface List<E> {}
爲例, List<E>
是 generic type, E
是 type parameter.List<String> stringList;
爲例, List<String>
是 parameterized type, 是 List<E>
的一個實例, String
是 type argument.泛型中將通配符(wildcard)分爲三類:數組
後二者也被統稱爲 bounded wildcard.
結合通配符, parameterized type 也能夠劃分爲三類.oracle
conceret type argument generic type ---------------------------> conceret parameterized type unbound type argument generic type ---------------------------> unbound parameterized type bounded type argument generic type ---------------------------> bounded parameterized type
Raw type 的存在是爲了兼容引入泛型以前的版本, 大多數時候你均可以不用考慮它.學習
嚴格說來, 泛型只存在於編譯期間, JVM 並不感知泛型. 在編譯時, 編譯器經過 type erasure 來消除 type parameter 和 type argument.
具體的處理方式是:spa
何爲上確界, 對於 upper bounded type paramter 而言, 是指其公共父類, <? extends Number> 對應的就是 Number.
對於其餘類型的 type paramter 而言, 由於 type argument 只能是引用類型(reference type), 而引用類型的公共父類是 Object, 因此其上確界都是 Object.code
咱們能夠從 Java Generics FAQs 的例子中看到具體的轉換過程. 左邊是原始的代碼, 右邊是通過 type erasure 轉換後的結果.orm
這個例子同時也反應了, 在 type erasure 的過程當中, 編譯器可能會按需加入 bridge method 和 type cast.htm
泛型的引入使得對象之間的繼承關係變得更復雜, 以下這個例子中的一部分就是錯誤的.對象
public class SuperDemo { public static void main(String args[]) { List<Number> a = new ArrayList<Number>(); ArrayList<Number> b = new ArrayList<Integer>(); List<? extends Number> c = new ArrayList<Integer>(); List<? super Number> d = new ArrayList<Object>(); List<? super Integer> e = d; } }
理論上, 泛型相關的繼承關係判斷須要從兩個緯度考慮:
具體而言. 對於 type argument 相同的狀況, generic type 之間的繼承關係決定兩個 parameterized type 的父子關係, 因此 List<Number>
是 ArrayList<Number>
的父類.
但 ArrayList<Number>
不是 ArrayList<Integer>
的父類, type argument 不一樣的狀況下, 泛型之間的繼承關係判斷會很複雜. 主要是因爲 wildcard 的存在, 致使 type argument 能夠表明一類類型, 因此要引入集合中的超集(superset)概念, 即一方所表明的全部類型徹底包含在以一方內.
最終的判斷標準是, 在 type argument 不相同的狀況下, 若是 type argument 是對方的超集, 並且 generic type 與對方相同或者是對方的父類, 那麼當前的 parameterized type 纔是對方的父類.
這時候再來回答如下的問題就會比較簡單了:
觀察泛型在異常處理和數組中的使用限制, 思考是什麼致使了這些限制, 在必定程度上能夠驗證本身以前的理解是否正確.
Java 的異常處理在運行時生效, 而 type erasure 發生在編譯期間, 因此大多數時候, 泛型在異常處理中並無用武之地.
Throwable
的子類是泛型.與異常處理相似, 數組在運行時保存了每一個元素的類型信息, 因此泛型數組也是一個沒有太大意義的概念. 雖然能夠定義一個數據的元素爲泛型, 但咱們僅能新建元素爲 unbound parameterized type 的泛型數組. 具體而言, 下例子中 line 1 和 line 2 合法, 但 line 3 是錯誤的.
List<String>[] a; List<?>[] b = new List<?>[10]; a = new List<String>[10]; // error
究其根本, 是由於數據的組成元素都應該是同一類型的: An array is a container object that holds a fixed number of values of a single type. 而同一 generic type 對應的不一樣實例實質上並不等價, 但通過 type erasure 後, 並不能在運行時區分出這些.
假如能新建元素爲 concerete parameterized type 的數組, 考慮以下案例.
List<String>[] stringLists = new List<String>[10]; stringLists[0] = new List<String>(); stringLists[1] = new List<Integer>();