(Java基礎篇都是根據《Java核心技術 卷I》再進行自己的總結歸納和思考寫出的)
泛型是什么?
泛型字面上就是多種類型,也就是多種類型都可以通用的代碼,它的目的就是為了實現代碼的重用。
比如說打印函數,在設計打印函數的時候我們要考慮到,可能傳入的參數類型是各種各樣(String、Integer...)的,這時候就是我們所說的“通用代碼”。打印函數就可以利用泛型,設計一個對任意數據類型通用的函數。泛型的實現原理其實是省略了參數類型-->Object類型-->參數類型的這個過程。若不使用泛型,可以通過將函數傳入的參數定為Object類型(也可以是對應類型的父類),那么也是一種通用的代碼,因為Object是所有類的超類,但是這樣設計函數會使得傳入非預期的類型對象也能夠執行此函數,并且還有經常強轉的麻煩。
如果使用泛型,就能夠記住傳入的參數類型,省卻了手動類型轉換的這一個過程。
但是要記住一點!!!在實際虛擬機執行過程中是走了這個轉換過程的。
使用泛型
此處使用泛型是指使用別人實現的泛型類/方法。
使用泛型類/方法是第一個階段,因為大多數用到泛型的類都已經封裝好了,許多程序也用不到新設計和實現泛型類和方法。
使用泛型類是比較簡單學習的,它就像普通類的工廠。
具體的使用可以參照集合類ArrayList、Map等用法,其實就是如何聲明,并使用里面的方法的問題。
使用泛型產生的效果就是限制了相應的數據類型。
實現和設計泛型類/方法
為了讓這個類/方法能夠被多種類型重用而在類和方法上添加泛型,那么首先我們要明白泛型在底層的邏輯
-
底層有關Java泛型轉換的兩個事實:
- 虛擬機里沒有泛型,只有普通的類和方法
虛擬機不會有泛型的概念,就只有哪個類型就是哪個類型 - 所有類型參數都用他們的限定類型替換
這就解釋了虛擬機怎么去翻譯相應的泛型類和泛型方法,它都是把里面的泛型翻譯到限定的類型,一般沒有限定都是以Object類型來翻譯泛型
這就是類型擦除。就是我們所設計的泛型到虛擬機里面都會被擦除成為普通的類和方法。
- 虛擬機里沒有泛型,只有普通的類和方法
知道在虛擬機中泛型會被類型擦除,那么下面的很多程序設計上的問題都是由類型擦除帶來的
-
泛型設計的局限
- 不能用基本類型實例化類型參數:因為基本類型沒辦法擦除嘛
- 運行時類型查詢只適用于原始類型:是不能直接判斷一個泛型類對象(如ArrayList<String>),里面的具體是什么類型的
- 不能創建參數化類型的數組:聲明是可以,但是創建不了ArrayList<String>[10],但是可以創建ArrayList<?>[10],以通配符的形式。原因還是類型擦除。
- 不能實例化類型變量:new T(...)是不能用的,因為這樣到虛擬機里面擦除之后都變成Object,沒意義了。
- 不能構造泛型數組:不能new T[2]這樣的寫法
- 泛型類的靜態上下文中類型變量無效:這里可以參考博客,講的很好
- ...
這里列舉了六種會比較常遇到的問題,也簡略解釋了一下,如果有不明白的可以參考《Java核心技術 卷I》泛型程序設計里面的約束和局限這一小節
-
泛型類&泛型方法有什么區別?
泛型類里面也可以有泛型的方法,但是呢類也可以不是泛型的而單單方法是泛型的,它們用法有什么區別?
其實泛型類的優勢就是能夠把類里面所有的方法泛型都給統一了,也就是說一旦類的參數化類型定下來了,這個對象所有的方法都必須是這個類型。單單是方法是泛型就沒有這個效果。
-
泛型的繼承
-
這里需要搞清楚的是,泛型類里面的參數化類型之間的繼承關系不是泛型類之間的繼承關系:
ArrayList<father>和ArrayList<son>沒有繼承關系
List<>和ArrayList<>才有繼承關系:繼承關系意義就是孩子把父親更具體化了
通配符
通配符的概念很容易讓人將泛型 T 和通配符 ? 搞暈。通配符?的目的呢就是解決上面所說的ArrayList<father>和ArrayList<son>問題。
假如現在有兩個對象:
ArrayList<father> a; ArrayList<son> b;
那么我只能往a對象里面放入father類型的對象,只能往b對象放入son類型的對象。但是呢如果是ArrayList<? extends father> c,那么就可以既存father也可以存son,
但是實驗好像有點問題,嗯,我下次專門寫一個探究一下通配符問題 -