編程思想 -- 第15章 -- 泛型

泛型

一般的類和方法,只能使用具體的類型,要麼是基本類型,要麼是自定義的類。如果要編寫可以應用於多種類型的編碼,這種刻板的限制對代碼的束縛就會很大。
在面向對象編程語言中,多態算是一種泛化機制。拘泥於單繼承體系,會使程序受限太多。有時候使用接口,對程序的約束還是太強。
泛型實現了參數化類型的概念,使代碼可以應用於多種類型。最初的目的是希望類或方法能夠具備最廣泛的表達能力,通過解耦類或方法與所使用的類型之間的約束。

一、與C++的比較
    Java中的泛型與C++比較的理由:1,瞭解C++的侷限是什麼,爲什麼會有這些限制。最終的目的是幫助你理解,Java泛型的邊界在哪裏。2,Java社區中,對C++的誤解可能會誤導你,令你在理解泛型的意圖時產生偏差。
    
二、簡單泛型
    泛型出現最引人注目的原因:爲了創造容器。泛型的主要目的之一就是用來指定容器要持有什麼類型的對象,而且由編譯器來保證類型的正確性。
    僅一次方法調用就能返回多個對象:將一組對象直接打包存儲於其中的一個單一對象,稱爲元組。這個容器允許讀取其中元素,但是不允許向其中存放新對象。元組可以具有任意長度,元組中的對象可以是任意不同的類型。元組隱含地保持了其中元素的次序。
    一個堆棧類:LinkedList<T>和Stack<T>
    
三、泛型接口
    泛型也可以應用於接口。例如生成器(generator),這是一種專門負責創建對象的類。一般一個生成器只定義一個方法,該方法用以產生新的對象。實際上,這是工廠方法設計模式的一種應用。當使用生成器創建新的對象時,它不需要任何參數,而工廠方法一般需要參數。也就是說,生成器無需額外的信息就知道如何創建對象。
    
四、泛型方法
    泛型方法使得該方法能夠獨立於類而產生變化。一個基本的指導原則:無論何時,只要你能做到,就應該只使用泛型方法。
    當使用泛型類時,必須在創建對象的時候指定類型參數的值,而使用泛型方法的時候,通常不必指明參數類型,因爲編譯器會爲我們找出具體的類型。這稱爲參數類型推斷。
    
五、匿名內部類
    泛型還可以應用於內部類及匿名內部類。
    
六、構建複雜模型
    泛型的一個重要好處就是能夠簡單而安全地創建複雜的模型。
    
七、擦除的神祕之處
    在泛型代碼內部,無法獲得任何有關泛型參數類型的信息。java泛型使用擦除來實現,當你使用泛型時,任何具體的類型信息都被擦除,唯一知道的就是你在使用一個對象。
    泛型類型只有在靜態烈性檢查期間纔出現,在此之後,程序中的所有泛型類型都將被擦除,替換爲他們的非泛型上界。
    擦除和遷移兼容性意味着,使用泛型不是強制的。
    因爲擦除在方法體中移除了類型信息,所以在運行時的問題就是邊界:即對象進入和離開方法的地點。這些正是編譯器在編譯期執行類型檢查並插入轉型代碼的地點。
    
八、擦除的補償
    擦除丟失了在泛型代碼中執行某些操作的能力,任何情況下運行時需要知道確切類型信息的操作都將無法工作。偶爾可以繞過這些問題來編程,但是有時必須通過引入類型標籤來對擦除進行補償。這意味着你需要顯示地傳遞你的類型的Class對象,以便你可以在類型表達式中使用它。
    不能創建泛型數組。一般的 解決方案是在任何想要創建泛型數組的地方都使用ArrayList。
    
九、邊界
    邊界使得你可以在用於泛型的參數類型上設置限制條件。儘管這使得你可以強制規定泛型可以應用的類型,但是其潛在的一個更重要的效果是你可以按照自己的邊界類型來調用方法。
    
十、通配符
    通配符引用的是明確的類型,它意味着某個對象的引用沒有指定的具體類型。
    超類型通配符<? super T>可以聲明通配符是由某個特定類的任何基類來界定的。
    無界通配符<?> 意味着任何事物,無界通配符好像等價於使用原生類型。
    
十一、問題
    任何基本類型都不能作爲類型參數。解決之道是使用基本類型的包裝器類。
    一個類不能實現同一個泛型接口的兩種變體,由於擦除的原因,這兩個變體會成爲相同的接口。
    使用帶有泛型類型參數的轉型或instanceof不會有任何效果。
    由於擦除的原因,重載方法將產生相同類型簽名。
    
十二、自限定的類型
    不能直接繼承一個泛型參數,但是可以繼承在其自己的定義中使用這個泛型參數的類。自限定的參數可以保證類型參數必須與正在被定義的類相同。如果使用自限定,就應該瞭解這個類所用的類型參數將與使用這個參數的類具有相同的基類型。這會強制要求使用這個類的每個人強制遵循這種形式。
    自限定類型的價值在於它們可以產生協變參數類型--方法參數類型會隨子類而變化。
    
十三、動態類型安全
    泛型容器的舊式代碼仍舊可能會破壞你的容器,類型檢查問題可以用java.util.Collections的靜態方法CheckedCollection(),checkedList(),checkedMap(),checkedSet(),checkedSortedMap()和checkedSortedSet().這些方法每一個都會將你希望動態檢查的容器當做第一個參數接受,並將你希望強制要求的類型作爲第二參數接受。
    
十四、異常
    由於擦除原因,將泛型應用於異常是非常受限的。catch語句不能捕獲泛型類型的異常,因爲在編譯時期和運行時期都必須知道異常的確切類型。反射類也不能直接或間接繼承自Throwable。但是類型參數可能會在一個方法的throws字句中用到,這使得
 你可以編寫隨檢查型異常的類型二發生變化的泛型代碼。如果不能參數化所拋出的異常,由於檢查型異常的緣故,不能編寫出這種泛化的代碼。
    
十五、混型
    混型概念是混合多個類的能力,以產生一個可以表示混型中所有類型的類,它將使組裝多個類變得簡單易行。
    混型的價值之一是它們可以將特性和行爲一致地應用於多個類之上。如果想在混型類中修改某些東西,作爲一種意外的好處,這些修改將會應用於混型所應用的所有類型之上。
    與接口混合:使用接口來產生混型效果。
    使用裝飾器模式:裝飾器使用分層對象來動態透明地向單個對象中添加責任。裝飾器通過使用組合和形式化結構來實現,而混型是基於繼承。
    與動態代理混合:通過使用動態代理,所產生的類的動態類型將會是已經混入的組合類型,由於動態代理的限制,每個被混入的類都必須是某個接口的實現。
    
十六、潛在類型機制
    潛在類型機制是一種代碼組織和複用機制。有了它編寫出的代碼相對於與沒有它寫出的代碼,能夠更容易地複用。
    
十七、對缺乏潛在類型機制的補償
    java不支持潛在類型機制,但是這並不意味着有界泛型代碼不能再不同的類型層次結構之間應用。我們仍舊可以創建真正的泛型代碼,但是這需要一些額外的努力。
    反射:通過反射能夠動態地確定所需要的方法收可用並調用它們。他甚至能夠處理Mime只具有一個必需的方法這一事實,並能夠部分實現其目標。
    將一個方法應用於序列:反射提供了一些有趣的可能性,但是它將所有的類型檢查都轉移奧了運行時。如果能夠實現編譯器類型檢查,這通常會更符合要求。
    
十八、將函數對象用作策略
    使用策略設計模式,可以產生更優雅的代碼,它將變化的事物完全隔離到了一個函數對象中。
    函數對象的價值在於,與普通方法不同,它們可以傳遞出去,並且還可以擁有在多個調用之間持久化的狀態。
    
十九、總結:轉型真的如此之糟嗎?
    使用泛型類型機制的最吸引人的地方,就是在使用容器類的地方,這些類包括各種List,Set,Map等中。
    泛型的通用語言特性的目的在於可表達性,而不僅僅是爲了創建類型安全的容器。類型安全的容器是能夠創建更通用代碼這一能力所帶來的副作用。
    

    
    
    
    
   

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章