《JAVA編程思想》5分鐘速成:第15章(泛型)

第十五章、泛型

泛型

(generics)的概念是Java SE5的重大變化之一。泛型實現了參數化類型(parameterized types)的概念,使代碼可以應用於多種類型。“泛型”這個術語的意思是:“適用於許多許多的類型”。


1.與c++的比較

c++的模板、Java泛型的邊界


2.簡單泛型(泛型類)

  • 泛型的產生背景 :創造容器類是促成泛型出現的一個重要原因。
  • 泛型的核心概念 :泛型的主要目的之一就是用來指定容器要持有什麼類型的對象,再由編譯器來保證類型的正確性。

與其使用Object我們更喜歡暫時不指定類型,而是稍後再決定具體使用什麼類型。要達到這個目的,要使用類型參數

public class Holder { private T a; public Holder(T a) {this.a = a;} public void set(T a) {this.a = a;} public T get() {return a;} }

2.1 一個元祖類庫

  • 元祖 :它是將一組對象直接打包存儲於其中的一個單一對象。這個容器對象允許讀取其中元素,但是不允許向其中存放新的對象。元祖也被稱為數據傳送對象或信使。元祖可以具有任意長度,其中的元素可以是任意不同的類型。還可以利用繼承機制實現長度更長的元祖。
  • 為了使用元祖,你只需定義一個長度適合的元祖,將其作為方法的返回值,然後在return語句中創建該元祖,並返回即可。

2.2 一個堆棧類

鏈表節點-泛型-嵌套類實現:

<code>private static class Node {    U item;    Node next;    Node() {}    Node(U item, Node next) {this.item = item;this.next = next;}    public boolean end() {return item==null && next ==null;}} 
/<code>


2.3 RandomList


3.泛型接口

  • 泛型也可以應用於接口。例如 生成器(generator),這是一種專門負責創建對象的類。實際上,這是 工廠方法設計模式 的一種應用。不過,當使用生成器創建新的對象時,它不需要任何參數,而工廠方法一般需要參數。
  • 一般而言,一個生成器只定義一個方法,該方法用以產生新的對象。

public interface Generator { T next(); }


4.泛型方法

  • 概念:泛型方法:即含參數化類型
    (parameterized types)的參數或者返回值方法。

舉例:

方法參數為泛型:public void func(T x){};

方法返回值為泛型:public List list(){};

  • 場景:泛型方法所在類,即可以是泛型類,也可以不是泛型類。
  • 原則:泛型方法使得該方法能夠獨立於類而產生變化。如果使用泛型方法可以取代整個類泛型化,那麼就應該只使用泛型方法,因為它可以使事情更清楚明白。


4.1 槓桿利用類型參數推斷

  • 類型參數推斷:使用泛型類時,創建對象時必須指定類型參數的值<classdemo>,但使用泛型方法時,通常不必指明參數類型,編譯器會自動找出具體的類型。/<classdemo>
  • 類型參數推斷的適用場景:類型參數推斷只對賦值操作(=)有效,其他場景(??比如作為方法參數傳遞時)無效。
  • 顯式的類型說明:調用泛型方法時,可以顯式的指明類型(應用極少)。

eg:

<code>public class Demo{public static  Map map(){return new HashMap();}}func(Demo.<person>>map());  //顯式的類型說明--用法!/<person>/<code>


4.2 可變參數與泛型方法

  • 泛型方法與可變參數列表能夠很好地共存

eg: public static List makeList(T... args){}


4.3 用於Generator的泛型方法

<code>public interface Generator {    T next();}public class Generators {    public static  Collection fill(Collection coll, Generator gen, int n) {        for(int i = 0; i < n; i ++)            coll.add(gen.next());        return coll;    }}/<code>

4.4 一個通用的Generator

<code>public class BasicGenerator implements Generator {    private Class type; //Class對象    public BasicGenerator(Class type) {        this.type = type;    }@override    public T next() {        try {            return type.newInstance();//通過Class對象生成實例Instance        } catch(Exception e) {            throw new RuntimeException(e);        }    }    public static  Generator create(Class type) {        return new BasicGenerator(type);    }}/<code>

4.5 簡化元組的使用

4.6 一個Set實用工具

  • EnumSet:Java SE1.5新增容器,可以直接從Enum創建Set。


5.匿名內部類

  • 泛型還可以應用於內部類以及匿名內部類。
<code>public interface Generator {    T next();}public static Generator<customer> generator(){return new Generator<customer>{ //泛型類創建對象時,必須指明類型public Customer next(){ return new Customer();} //泛型方法調用時,自動類型推斷}}/<customer>/<customer>/<code>


6.構建複雜模型

  • 泛型的一個重要好處是能夠簡單(參數化類型的容器)而安全(類型安全,編譯器自動檢測)地創建複雜的模型。


7.擦除的神秘之處

  • 根據JDK文檔的描述,Class.getTypeParameters() 將返回一個 TypeVarible 對象數組,表示有泛型聲明的類型參數...這只是表示用作參數佔位符的標識符,這並非有用的信息。所以
    在泛型代碼內部,無法獲得任何有關泛型參數類型的信息。
  • 泛型-類型參數的擦除: Java泛型是使用擦除來實現的,這意味著當你在使用泛型時,任何具體的類型信息都被擦除了,你唯一知道的就是你在使用一個對象。因此,List<string> 和 List<integer> 在運行時實際上是相同的類型。這兩種類型都被擦除成它們的原生類型,即 List。/<integer>/<string>

7.2 遷移兼容性。

  • 泛型類型只有在靜態類型檢查期間(編譯期?)才出現。在此之後(運行期?),程序中的所有泛型類型都被擦除,替換為他們的非泛型上界(第一個邊界)。普通類型被擦除成 Object。

eg: 類型參數的擦除:

會被類型擦除為 HasF

會被類型擦除為 HasE??

  • 擦除的核心動機是它使得泛化的客戶端可以用非泛化的類庫來使用,反之亦然,這經常被稱為 遷移兼容性


7.3 擦除的問題

  • 擦除的理由:在不破壞現有java類庫的情況下,將泛型融入到Java語言中。
  • 擦除的代價:是顯著的。不能用於顯式地引用運行時類型的操作之中,例如轉型、instanceof操作和 new表達式。因為所有關於參數的類型信息都丟失了。


7.4 邊界處的動作

  • 對於泛型中創建數組,推薦使用:Array.newInstance() (WHY?? )
<code> 


8.擦除的補償

  • 擦除丟失了在泛型代碼中執行某些操作的能力。任何在運行需要周到確切類型信息的操作都將無法工作。
<code>public class Erased {    private final int SIZE = 100;    public static void f(Object arg) {        if(arg instanceof T) {              //ERROR,可改為Class對象isInstance()            T var = new T();                //ERROR,可改為Class對象newInstance()            T[] array = new T[SIZE];        //ERROR,可改為使用new ArrayList()            T[] array = (T)new Object[SIZE]; //unchecked warning        }    }}/<code>


  • 擦除補償:使用 instanceof 的嘗試是失敗的,因為其類型信息已經被擦除了。如果引入類型標籤,就可以轉用動態的 isInstance() 。
  • <code>public class ClassObject {    Class kind;    public boolean f(Object arg) {        return kind.isInstance(arg); //擦除補償的用法舉例    }}/<code>


    9.邊界

    • 泛型邊界:使得你 可以在用於泛型的參數類型上設置限制條件。
    • 泛型複用extends關鍵字:不同於常規的extends關鍵字語法&含義。舉例:
    • 通配符?:定義: extends Fruit> ,可使用:<orange>
    • 超類型通配符super關鍵字:舉例:定義:,可使用<fruit>
    • 無界通配符>:意味任何對象:List>


    10. 泛型問題

    • 任何基本數據類型 不能作為泛型類型參數(->可改為使用包裝類)
    • 一個類不能實現同時實現泛型接口的兩種變體(泛型-類型擦除後,擦除後的方法不能重載)。
    • 轉型(upcasting&downcasting)+ instanceof() :均不可用!
    • 方法重載:方法參數(泛型)會被擦除,不可用!


    11. 自限定類型

    • 自限定類型:確保類型參數必須與正在被定義的類相同。
    • 參數協變:自限定類型的價值,在於產生協變參數類型(方法參數類型會隨著子類變化而變化)
    <code>


    12. 動態類型安全

    • 泛型容器:受檢查的容器在試圖插入類型不正常的對象時,拋出ClassCastException。

    13. 異常


    14. 混型

    • AOP:面向方面(切面)的編程,Aspect Oriented Programming:通過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容.
    • 混型:混合多個類的能力,將特性和行為一致性的應用多個類上。
    • 裝飾者模式:GoF23之一:使用分層對象來動態透明的向單個對象添加責任。


    15. 將函數對象用作策略

    • 策略模式:GoF23之一:將變化的事物 完全隔離到一個函數對象中。
    • 函數對象:就是某種程度上,行為像函數的對象。
    • 函數對象的價值:與普通方法不同,他們可以被傳遞出去,還可以擁有在多個調用之間持久化的狀態。
    <code>


    《JAVA編程思想》5分鐘速成:第15章(泛型)


    分享到:


    相關文章: