Java核心技術卷一基礎知識-第12章-泛型程序設計-讀書筆記

第12章 泛型程序設計

本章內容:
* 爲什麼要使用泛型程序設計
* 定義簡單泛型類
* 泛型方法
* 類型變量的限定
* 泛型代碼和虛擬機
* 約束與侷限性
* 泛型類型的繼承規則
* 通配符類型
* 反射和泛型
  1. 使用泛型機制編寫的程序代碼要比那些雜亂地使用Object變量,然後再進行強制類型轉換的代碼具有更好的安全性和可讀性。

12.1 爲什麼要使用泛型程序設計

  1. 泛型程序設計(Generic programming)意味着編寫的代碼可以被很多不同類型的對象所重用。

12.2 定義簡單泛型類

  1. 一個泛型類(generic class)就是具有一個或多個類型變量的類。
  2. 類型變量使用大寫形式,且比較短,這是很常見的。在Java庫中,使用變量E表示集合的元素類型,K和V分別表示表的關鍵字與值的類型。T(需要時還可以用臨近的字母U和S)表示“任意類型”。
  3. 用具體的類型替換類型變量就可以實例化泛型類型。
  4. 泛型類可看作普通類的工廠。

12.3 泛型方法

  1. 泛型方法的類型變量放在修飾符(如public static)的後面,返回類型的前面。
  2. 泛型方法可以定義在普通類中,也可以定義在泛型類中。
  3. 當調用一個泛型方法時,在方法名前的尖括號中放入具體的類型。在大多數情況下,方法調用中可以省略具體的類型。

12.4 類型變量的限定

  1. <T extends BoundingType>表示T應該是綁定類型的子類型(subtype)。T和綁定類型可以是類,也可以是藉口。選擇關鍵字extends的原因是更接近子類的概念。
  2. 一個類型變量或通配符可以有多個限定。限定類型用“&”分隔,而逗號用來分隔類型變量。
  3. 在Java的繼承中,可以根據需要擁有多個接口超類型,但限定中至多有一個類。如果用一個類作爲限定,它必須是限定列表中的第一個。

12.5 泛型代碼和虛擬機

  1. 虛擬機沒有泛型類型對象-所有對象都屬於普通類。在泛型實現的早期版本中,甚至能夠將使用泛型的程序編譯爲在1.0虛擬機上運行的類文件!這個向後兼容性在Java泛型開發的後期被放棄了。
  2. 無論何時定義一個泛型類型,都自動提供了一個相應的原始類型(raw type)。原始類型的名字就是刪去類型參數後的泛型類型名。擦除(erased)類型變量,並替換爲限定類型(無限定的變量用Object)。
  3. 原始類型用第一個限定的類型變量來替換,如果沒有給定限定就用Object替換。
  4. 爲了提高效率,應該將標籤(tagging)接口(即沒有方法的接口)放在邊界列表的末尾。

12.5.1 翻譯泛型表達式

  1. 當程序調用泛型方法時,如果擦除返回類型,編譯器插入強制類型轉換。
  2. 當存取一個泛型域時也要插入強制類型轉換。

12.5.2 翻譯泛型方法

  1. 有關Java泛型轉換的事實:
    • 虛擬機中沒有泛型,只有普通的類和方法。
    • 所有的類型參數都用它們的限定類型替換。
    • 橋方法被合成來保持多態。
    • 爲保持類型安全性,必要時插入強制類型轉換。

12.5.3 調用遺留代碼

  1. 設計Java泛型類時,主要目標是允許泛型代碼和遺留代碼之間能夠互操作。
  2. @SuppressWarnings(“unchecked”)關閉對代碼的檢查。

12.6 約束與侷限性

12.6.1 不能用基本類型實例化類型參數

  1. 不能用類型參數代替基本類型。

12.6.2 運行時類型查詢只適用於原始類型

  1. 虛擬機中的對象總有一個特定的非泛型類型。因此,所有的類型查詢只產生原始數據。
  2. 無論何時使用instanceof或涉及泛型類型的強制類型轉換表達式都會看到一個編譯器警告。
  3. getClass方法總是返回原始類型。

12.6.3 不能創建參數化類型的數組

  1. Pair<String>[] table=new Pair<String>[10];//error
  2. 不允許創建參數化類型的數組,而聲明類型爲Pair[]的變量仍是合法的。不過不能用new Pair<String>[10]初始化這個變量。
  3. 如果需要收集參數化類型對象,只有一種安全而有效的方法:使用ArrayList:ArrayList<Pair<String>>

12.6.4 Varargs警告

  1. 向參數個數可變的方法傳遞一個泛型類型的實例:
    public static <T> void addAll(Collection<T> coll,T... ts)
    {
     for(t:ts) coll.add(t);
    }
    
    只會得到一個警告,而不是錯誤。
    可以採用兩種方法來抑制這個警告。一種方法是爲包含addAll調用的方法增加標註@SuppressWarnings(“unchecked”)。或者在Java SE 7中,還可以用@SafeVarargs直接標註addAll方法。
    對於只需要讀取參數數組元素的所有方法,都可以使用這個標註。

12.6.5 不能實例化類型變量

  1. 不能使用像new T(…),new T[…]或T.class這樣的表達式中的類型變量。
  2. 不能構造泛型數組。
  3. 如果數組僅僅作爲一個類的私有實例域,就可以將這個數組聲明爲Object[],並且在獲取元素時進行類型轉換。

12.6.6 泛型類的靜態上下文中類型變量無效

  1. 不要在靜態域或方法中引用類型變量。

12.6.7 不能拋出或捕獲泛型類的實例

  1. 泛型類擴展Throwable都是不合法的。
  2. catch子句中不能使用類型變量。
  3. 在異常規範中使用類型變量時允許的。
  4. Java異常處理的一個基本原則是,必須爲所有已檢查異常提供一個處理器。不過可以利用泛型消除這個限制。關鍵在於以下方法:
    @SuppressWarnings("unchecked")
    public static <T extends Thowable> void throwAs(Throwable e)throws T
    {
     throw (T)e;
    }
    
  5. 通過使用泛型類、擦除和@SuppressWarnings標註,就能消除Java類型系統的部分基本限制。

12.6.8 注意擦除後的衝突

  1. 泛型規範原則:“要想支持擦除的轉換,就需要強制限制一個類或類型變量不能同時成爲兩個接口類型的子類,而這兩個接口是同一個接口的不同參數化。”

12.7 泛型類型的繼承規則

  1. 永遠可以將參數化類型轉換爲一個原始類型。
  2. 泛型類可以擴展或實現其他的泛型類。

12.8 通配符類型

  1. 固定的泛型系統使用並不是很好,可以使用“通配符類型”解決。

12.8.1 通配符的超類型限定

  1. 通配符限定與類型變量限定十分類似,但是,還有一個附加的能力,即可以指定一個超類型限定(supertype bound)。super。
  2. 帶有超類型限定的通配符可以向泛型對象寫入,帶有子類型限定的通配符可以從泛型對象讀取。

12.8.2 無限定通配符

  1. 還可以使用無限定通配符,Pair<?>Pair<?>Pair本質的不同在於:可以用文藝Object對象調用原始的Pair類的setObject方法。

12.8.3 通配符捕獲

  1. 通配符不是類型變量,因此,不能在編寫代碼中使用“?”作爲一種類型。
  2. 通配符捕獲只有在由許多限制的情況下才是合法的。編譯器必須能夠確信通配符表達的是單個、確定的類型。

12.9 反射和泛型

  1. Class類是泛型的。String.class實際上是一個Class<String>類的對象(事實上,是唯一的對象)。
  2. java.lang.Class 1.0
    • T newInstance() 5.0
      返回默認構造器構造的一個新實例。
    • T cast(Object obj) 5.0
      如果obj爲null或有可能轉換爲類型T,則返回obj;否則拋出BadCastException異常。
    • T[] getEnumConstants() 5.0
      如果T是枚舉類型,則返回所有值組成的數組,否則返回null。
    • Class<? super T> getSuperClass() 5.0
      返回這個類的超類。如果T不是一個類或Object類,則返回null。
    • Constructor getConstructor(Class… parameterTypes) 5.0
    • Constructor getDeclaredConstructor(Class… parameterTypes) 5.0
      獲得公有的構造器,或帶有給定參數類型的構造器。
  3. java.lang.reflect.Constructor 1.1
    • T newInstance(Object… parameters) 5.0
      返回用指定參數構造的新實例。

12.9.1 使用Class參數進行類型匹配

12.9.2 虛擬機中的泛型類型信息

  1. 包含在類文件中,讓泛型反射可用的類型信息與舊的虛擬機不兼容。
  2. 爲了表達泛型類型聲明,Java SE 5.0在java.lang.reflect包中提供了一個新的接口Type。這個接口包含下列子類型:
    • Class類,描述具體類型。
    • TypeVariable接口,描述類型變量(如T extends Comparable<? super T>)。
    • WildcardType接口,描述通配符(如? super T)。
    • ParameterizedType接口,描述泛型類或接口類型(如Comparable<? super T>)。
    • GenericArrayType接口,描述泛型數組(如T[])。
      注意,最後4個子類型時接口,虛擬機將實例化實現這些接口的適當的類。
  3. java.lang.Class 1.0
    • TypeVariable[] getTypeParameters() 5.0
      如果這個類型被聲明爲泛型類型,則獲得泛型類型變量,否則獲得一個長度爲0的數組。
    • Type getGenaricSuperclass() 5.0
      獲得被聲明爲這一類型的超類的泛型類型;如果這個類型是Object或不是一個類類型(class type),則返回null。
    • Type[] getGenericInterfaces() 5.0
      獲得被聲明爲這個類型的接口的泛型類型(以聲明的次序),否則,如果這個類型沒有實現接口,返回長度爲0的數組。
  4. java.lang.reflect.Method 1.1
    • TypeVariable[] getTypeParameters() 5.0
      如果這個方法被聲明爲泛型方法,則獲得泛型類型變量,否則返回長度爲0的數組。
    • Type getGenericReturnType() 5.0
      獲得這個方法被聲明的泛型返回類型。
    • Type[] getGenericParameterTypes() 5.0
      獲得這個方法被聲明的泛型參數類型。如果這個方法沒有參數,返回長度爲0的數組。
  5. Java.lang.reflect.TypeVariable 5.0
    • String getName()
      獲得類型變量的名字。
    • Type[] getBounds()
      獲得類型變量的子類限定,否則,如果該變量無限定,則返回長度爲0的數組。
  6. Java.lang.reflect.WildcardType 5.0
    • Tyep[] getUpperBounds()
      獲得這個類型變量的子類(extends)限定,否則,如果沒有子類限定,則返回長度爲0的數組。
    • Type[] getLowerBounds()
      獲得這個類型變量的超類(super)限定,否則,如果沒有超類限定,則返回長度爲0的數組。
  7. Java.lang.reflect.ParameterizedType 5.0
    • Type getRawType()
      獲得這個參數化類型的原始類型。
    • Type[] getActualTypeArguments()
      獲得這個參數化類型聲明時所使用的類型參數。
    • Type getOwnerType()
      如果是內部類型,則返回其外部類型,如果是一個頂級類型,則返回null。
  8. Java.lang.reflect.GenericArrayType 5.0
    • Type getGenericComponentType()
      獲得聲明該數組類型的泛型組合類型。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章