只要瞭解了泛型的一般使用情況就能夠解決多半的問題。 所以, 首先我們來瞭解一下什麼是泛型、為什麼要使用它以及應用方法。
什麼是泛型?
試想一個簡單的添加方法(method), 如下:
long, float 或 double 類型並不能當作輸入傳給這個方法。
如果從該方法中抽象出資料類型, 就可以得到一個新的方式, 如下。
在這裡,
現在考慮資料結構, 簡單起見, 我們來想一想陣列。 我們能夠創建一個任意類型的陣列嗎?不可以。 我們只能創建一個整數陣列、浮點數陣列或者其他一種特定類型的陣列。 好了, 忘掉所有程式設計語言裡實現陣列的方法, 然後問一個問題:“我們是否可以從這個資料結構中抽象出一種資料類型?”
答案是肯定的。 Java 中的 ArrayList 就是做這件事的一種類。
雖然我們用 ArrayList 作為例子, 但由於其複雜性, 我們不會討論他們具體是怎麼實現的。 我們會借鑒一個盒子並思考怎麼把這個盒子做出來, 而這個盒子就是某個特定類型的通用框架(a Generic box from a Specific Typed box)。
思考以下代碼, 將一個字串放進特定字串框架(SpecifizedStringBox)物件中, 然後以此獲得一個字串。
現在, 如果從該物件中抽取其資料類型“Type”, 就得到一個由以下代碼代表的通用框架(GenericBox, 也就是泛型), 而該框架可以使用 String、Integer、Boolean 等任意資料類型。
所以, 使用泛型, 就是要從某個方法(method)或者類(class)中, 抽象出一種適用於任意類型的通用方法/類。
為什麼要用泛型?
簡單點的答案就是, 通過泛型抽象資料類型後, 你的代碼可以重複使用並且易於維護。
泛型應用在什麼地方?
看起來似乎通過重構已有特定類型的方法或框架, 就能應用泛型。 在處理資料結構和原始資料類型時, 似乎還挺容易, 但是我們總會在各不相同的類中建立大量的資料類型。
本文就將帶你瞭解一些典型的泛型用例, 包括其使用場景, 也可以讓你在遇到同類型問題時能夠合理應用泛型。
Java 在 JDK 5.0 中引用泛型的目的在於:
類型安全性(Type safety):一旦使用類型參數後, 在該方法或框架中就不存在其他的資料類型, 同時也避免了類型轉化的需求;
通用程式設計及參數的多態性。
C++ 的模版程式設計能幫我們實現通用程式設計及參數的多態性, 根據資料的類型(預定義或用戶定義的)轉化同樣的演算法模型, 達到複用同一個代碼或程式的目的。 在 Java 中也可以使用類似的方法。
現在來看一下幾個常用的泛型用例。
用例 1 : 泛型的第一級別用法是演算法和資料類型
演算法和資料結構並行, 資料類型的微小變化可能會改變一個演算法的複雜性。
資料結構中的資料有類型, 用泛型將這種類型抽取出來, 可以作為類型參數。 而演算法的輸入參數也具有資料類型, 同樣, 通過泛型可以將該類型從輸入參數中抽象出來。 因此, 泛型適用於使用特定資料結構的任意一種演算法。
不過事實上, 泛型主要用於 Java 的集合 API。
如果你自己寫資料結構, 那麼一定試試利用泛型。 除了Java 的集合 API, 你也會在 Guava、Apache Common Collection、FastUtils、JCtools 和 Eclipse Collection 裡發現其他對泛型更好的應用。
用例 2 :數值輸入框或者單個元素的容器
具備可通用類型的資料結構, 可以稱之為泛型框架(Generics Boxes)。 例如 ArrayList、LinkedList 等等這樣的類就代表資料類型,同時為他們同類型的資料起著泛型框架的作用。
有時候,通用框架以單個元素而不是集合的形式出現。諸如 Map 映射中的輸入
ThreadLocal 和 AtomicReference 在適用於併發訪問演算法的單元素容器中,是非常好的例子。
類似的用法有時合理,而有一些則不太適
用。 一個盒子在早期確實可以容納任何類型的物品,但現在會將其進行分類:這個盒子用來裝玩具,而下一個盒子用來裝筆,等等。
杯子是很好的例子,可以把它比做即時物件類型的依託物(Holder),它可以裝茶、咖啡或者任何飲料。公交上可以坐男人和女人,如果讓公交具備類型安全性且只允許女人上車,那麼我們可以稱之為女士公交。這種比喻可能有點欠妥,但它提出了商業用例,尤其是封裝器或者依託物也具有應用泛型的可能。嘗試詢問業務的封裝或依託是否有使用資料結構的傾向,如果有,那麼使用泛型會更好。
用例類型 3 :抽象類別型的泛型工具方法
泛型演算法不一定總是和特定的資料結構或演算法綁定在一起。有時,基於實際應用的滿意度,它還可以應用在大多數抽象資料結構組中。
在 Java 中就有該Collections工具類。
查看以下方法,瞭解什麼方法能適用:
Collection Factories Methods, Empty/Singleton
emptyList, emptyMap, emptySet
singleton, singletonList, singletonMap
封裝方法(Synchronized, UnModifiable, Checked Collection):
synchronizedCollection, synchronizedSet, synchronizedMap
unmodifiableCollection, unmodifiableSet, unmodifiableList
checkedCollection, checkedList, checkedSet
還有一些泛型方法,可歸為四大類:
1. 更改清單中的元素順序:reverse,rotate,shuffle,sort,swap;
2. 更改清單內容:copy, fill, replaceAll;
3. 在集合中尋找極值:最大值,最小值;
4. 在列表中查找特定值:binarySearch,indexOfSubList,lastIndexOfSubList。
由於他們適用於列表中的任意類型,這些都是可複用的功能。我們會發現,大多數集合都適用泛型方法。
用例類型 4 :泛型方法用於類的分層並行結構中
Spring 框架中的 JpaRepository、CrudRepository 都已使用泛型構建,創建、更新、查找、查找所有、刪除等等,是適用於所有實體的泛型方法。
需要給每個實體創建一個並行資料訪問物件(DAO)類時,會出現類的分層並行結構(parallel hierarchy of classes)。 不過 DAO 模式並不是其出現的唯一情況。
如果為了提供更多可能的方法實例,我們可以通過將方法與物件解除聯繫的方式,來應用策略模式(Strategy Pattern)處理業務問題,這時類的分層並行結構就會出現。
每當我們添加一個新類,就會增加一個並行的測試用例。如果需要工廠,我們就添加一個並行工廠類。 類的分層並行結構出現在業務用例中。試想一輛新車,比如“大巴車”,把它添加到以下的車輛層級中時,可能還需要添加一個“大巴車司機”的類。
來看以下分層並行類和其泛型的例子:
用例類型 5 : 創建類型安全的異構容器
集合
而集合
在集合
像是 bean 容器,例外處理常式容器,或服務查找容器都是異構容器的示例,都可以使用泛型來進行類型安全化,方法既使用類物件作為鍵實現動態轉換。
例如 ArrayList、LinkedList 等等這樣的類就代表資料類型,同時為他們同類型的資料起著泛型框架的作用。有時候,通用框架以單個元素而不是集合的形式出現。諸如 Map 映射中的輸入
ThreadLocal 和 AtomicReference 在適用於併發訪問演算法的單元素容器中,是非常好的例子。
類似的用法有時合理,而有一些則不太適
用。 一個盒子在早期確實可以容納任何類型的物品,但現在會將其進行分類:這個盒子用來裝玩具,而下一個盒子用來裝筆,等等。
杯子是很好的例子,可以把它比做即時物件類型的依託物(Holder),它可以裝茶、咖啡或者任何飲料。公交上可以坐男人和女人,如果讓公交具備類型安全性且只允許女人上車,那麼我們可以稱之為女士公交。這種比喻可能有點欠妥,但它提出了商業用例,尤其是封裝器或者依託物也具有應用泛型的可能。嘗試詢問業務的封裝或依託是否有使用資料結構的傾向,如果有,那麼使用泛型會更好。
用例類型 3 :抽象類別型的泛型工具方法
泛型演算法不一定總是和特定的資料結構或演算法綁定在一起。有時,基於實際應用的滿意度,它還可以應用在大多數抽象資料結構組中。
在 Java 中就有該Collections工具類。
查看以下方法,瞭解什麼方法能適用:
Collection Factories Methods, Empty/Singleton
emptyList, emptyMap, emptySet
singleton, singletonList, singletonMap
封裝方法(Synchronized, UnModifiable, Checked Collection):
synchronizedCollection, synchronizedSet, synchronizedMap
unmodifiableCollection, unmodifiableSet, unmodifiableList
checkedCollection, checkedList, checkedSet
還有一些泛型方法,可歸為四大類:
1. 更改清單中的元素順序:reverse,rotate,shuffle,sort,swap;
2. 更改清單內容:copy, fill, replaceAll;
3. 在集合中尋找極值:最大值,最小值;
4. 在列表中查找特定值:binarySearch,indexOfSubList,lastIndexOfSubList。
由於他們適用於列表中的任意類型,這些都是可複用的功能。我們會發現,大多數集合都適用泛型方法。
用例類型 4 :泛型方法用於類的分層並行結構中
Spring 框架中的 JpaRepository、CrudRepository 都已使用泛型構建,創建、更新、查找、查找所有、刪除等等,是適用於所有實體的泛型方法。
需要給每個實體創建一個並行資料訪問物件(DAO)類時,會出現類的分層並行結構(parallel hierarchy of classes)。 不過 DAO 模式並不是其出現的唯一情況。
如果為了提供更多可能的方法實例,我們可以通過將方法與物件解除聯繫的方式,來應用策略模式(Strategy Pattern)處理業務問題,這時類的分層並行結構就會出現。
每當我們添加一個新類,就會增加一個並行的測試用例。如果需要工廠,我們就添加一個並行工廠類。 類的分層並行結構出現在業務用例中。試想一輛新車,比如“大巴車”,把它添加到以下的車輛層級中時,可能還需要添加一個“大巴車司機”的類。
來看以下分層並行類和其泛型的例子:
用例類型 5 : 創建類型安全的異構容器
集合
而集合
在集合
像是 bean 容器,例外處理常式容器,或服務查找容器都是異構容器的示例,都可以使用泛型來進行類型安全化,方法既使用類物件作為鍵實現動態轉換。