深入淺出 Java 泛型之(一):前生今世

LoulanPlan

本文出自伯特的《LoulanPlan》,轉載務必注明作者及出處牲距。

對于 Java 開發(fā)者而言,泛型是必須掌握的知識點钥庇。泛型本身并不復雜牍鞠,但由于涉及的概念、用法較多上沐,所以打算通過系列文章去講解皮服,旨在全面、通俗的介紹泛型及其使用。如果你是初學者龄广,可以通過本文了解泛型硫眯,并滿足企業(yè)級開發(fā)的需求;如果你對泛型已有一定的了解择同,可以通過本文進行鞏固两入,加深對泛型的理解。

作為系列文章的第一篇敲才,本文將帶你了解 Java 泛型的前生今世裹纳,看看泛型的誕生之于開發(fā)者的意義。

1. 泛型之前:通用數(shù)據(jù)類型

對于集合框架中的 List 及其實現(xiàn)類紧武,想必大家都不陌生剃氧。同時,泛型誕生之后即被廣泛運用于 Java 集合框架阻星。所以朋鞍,我們就以 List 作為觀察對象,看看在泛型誕生之前妥箕,Oracel 的工程師們是如何進行設計的滥酥。

摘自 JDK 1.4 的 List.java 源碼:

public interface List extends Collection {
    //添加元素
    boolean add(Object o);
    //查詢元素
    Object get(int index);
}

可以看出 List 是通過 Object 類型管理的數(shù)據(jù),如此設計的好處顯而易見:

具備通用性畦幢,因為所有的類都是 Object 的直接或間接子類坎吻,所以適用于任意類型的對象

同時宇葱,弊端也是不可忽視的瘦真。下面就通過使用 List 存、取數(shù)據(jù)來看看都有哪些問題:

//構造對象
List list = new ArrayList();
//存
list.add(1);
list.add("2");//①
//取
int num1 = (int)list.get(0);
int num2 = (int)list.get(1);//②

由于使用 Object贝搁,編譯器無法判斷存吗氏、取數(shù)據(jù)的實際類型,導致上述幾行代碼暴露出許多問題:

  1. 無法限制存儲數(shù)據(jù)類型雷逆,不夠健壯:在 ① 處可以添加 String 類型數(shù)據(jù)弦讽,顯然是臟數(shù)據(jù);
  2. 取出時強轉代碼冗余膀哲,可讀性差:取出數(shù)據(jù)時必須顯示強轉為 int 類型往产;
  3. 由于 ① 處在編譯時無法檢查出錯誤,導致 ② 處的強轉在運行時引發(fā) ClassCastException某宪,安全性低仿村;

問題還真不少!

2. 泛型萌芽:數(shù)據(jù)類型的包裝

上述問題究其根本兴喂,是無法限制數(shù)據(jù)類型引起的蔼囊。也就是說焚志,如果我們基于 List 包裝出相應類型的 XxxList,就可以解決問題畏鼓。

舉個例子酱酬,包裝用于存儲 Integer 數(shù)據(jù)類型的 IntegerList

public class IntegerList {
    List list = new ArrayList();

    //限制外部只能添加整型數(shù)據(jù)
    public boolean add(Integer data) {
        return list.add(data);
    }

    //內部進行強轉,調用者可以直接賦值為整型
    public Integer get(int index) {
        return (Intrger)list.get(index);
    }
}

包裝內依然使用 List 管理數(shù)據(jù)云矫,但我們對外暴露的接口限制了數(shù)據(jù)類型膳沽,規(guī)避了直接訪問 List 的接口可能引發(fā)的問題。

下面一起來看看如何使用包裝類:

//構造對象
IntegerList list = new IntegerList();
//存
list.add(1);
list.add("2");//①
//取
int num1 = list.get(0);

怎么樣让禀,一個包裝類輕松解決問題:

  1. 在 ① 處試圖添加 String 類型數(shù)據(jù)挑社,會在編譯期進行類型檢查時報錯,導致編譯失斞沧帷痛阻;
  2. 在取出數(shù)據(jù)時,無需重復強轉腮敌,直接賦值給 int 類型的數(shù)據(jù)录平;
  3. 因為限制了 add() 方法的參數(shù)類型,所以不用擔心在 get() 時內部強轉會引發(fā)異常缀皱。

簡直完美。同理动猬,可以包裝出一系列 StringList, LongList啤斗,以及自定義數(shù)據(jù)的集合包裝類 PeopleList, DataList 等。

但人無完人赁咙,類亦無完類啊钮莲。包裝類雖解決了編碼上的數(shù)據(jù)類型問題,可在工程效率方面卻捉襟見肘:

  • 復用性低:每一個包裝類只適用于一種數(shù)據(jù)類型彼水,無法復用核心邏輯崔拥;
  • 維護成本高:復用性低必然會增加后期維護的成本。

仍需努力凤覆!

3. 泛型登場:參數(shù)化類型

雖然包裝類存在缺陷链瓦,但其對于理解泛型思想是很有意義的。不知 Oracle 的工程師們盯桦,是否受此啟發(fā)設計出的泛型呢慈俯?

如果你試著多寫幾個數(shù)據(jù)類型的包裝類,就會發(fā)現(xiàn)各包裝類之間的區(qū)別和聯(lián)系:

  1. 區(qū)別:數(shù)據(jù)類型不同拥峦;
  2. 聯(lián)系:操作數(shù)據(jù)的方法相同贴膘,即核心算法邏輯是一致的。

既然如此略号,如果我們能夠弱化數(shù)據(jù)類型刑峡,使其不再受具體的業(yè)務場景限制洋闽,就可以做到專注于通用的算法邏輯,從而提升復用性突梦。

那么诫舅,如何弱化數(shù)據(jù)類型呢?有人說了阳似,使用 Object 就很弱化啊骚勘。咳撮奏,麻煩你從頭開始看俏讹。。畜吊。

JDK 5(即 JDK 1.4 之后的 1.5) 引入了 泛型(Generic Type) 的概念泽疆,其通過“參數(shù)化類型”實現(xiàn)數(shù)據(jù)類型的弱化,使得程序內部不需要關心具體的數(shù)據(jù)類型玲献,而是讓業(yè)務在調用時作為參數(shù)傳入殉疼。泛型將傳入的數(shù)據(jù)類型傳遞給編譯器,這樣編譯器就可以在編譯期間進行類型檢查捌年,確保程序的安全性瓢娜,并且可以插入相應的強轉以避免開發(fā)人員顯示強轉。

上面這段話值得多讀幾遍礼预,尤其是“參數(shù)化類型”可以說是泛型的核心所在眠砾。如果還有點蒙沒關系,繼續(xù)往下看托酸。

Java 中方法的聲明大家都不陌生褒颈,如果某個方法需要對整數(shù)進行加法運算,我們可以在聲明方法時添加整數(shù)類型的參數(shù)励堡,外部調用時必須傳入相應的整數(shù)數(shù)據(jù)谷丸。這里,將數(shù)據(jù)抽象為參數(shù)的過程应结,可以理解為“參數(shù)化實參”刨疼。

那么,“參數(shù)化類型”可以理解為是“參數(shù)化數(shù)據(jù)”的進一步抽象:將數(shù)據(jù)類型抽象為參數(shù)鹅龄,即類型形參币狠。如此一來,數(shù)據(jù)類型可以像形參一樣砾层,在調用時動態(tài)指定漩绵。如此,就達到了使用通用邏輯動態(tài)處理不同數(shù)據(jù)類型的目的肛炮。

下面止吐,我們通過 JDK 源碼中有關泛型的運用來鞏固這一概念宝踪。

4. 泛型的簡單運用

泛型誕生后,即對 Java 集合框架進行了大刀闊斧的修改碍扔,引入了泛型瘩燥。下面仍然以 List 作為觀察對象,看看泛型帶來了哪些改變不同。

//摘自 JDK 5 版本的 List 源碼
public interface List<E> extends Collection<E> {
    //添加元素
    boolean add(E e);
    //指定下標查詢元素
    E get(int index);
    //指定下標移除元素
    E remove(int index);
}

可以看出厉膀,List<E> 通過在類 List 后追加 <> 標識其為泛型類,包含的元素 E 即“類型形參“二拐,以支持開發(fā)者在使用時指定實際類型服鹅。下面看看在代碼中如何使用泛型 List

//構造對象
List<Integer> list = new ArrayList();
//存
list.add(1);
list.add("2");//①
//取
int num1 = list.get(0);
int num2 = list.get(1);

首先,我們構造了 List<Integer> 類型的對象百新,所以在運行時 List<E> 中的形參會被當做 Integer 去出處理企软,我們可以想象出一個虛擬的 List 類:

public interface List extends Collection<E> {
    boolean add(Integer e);
    Integer get(int index);
    Integer remove(int index);
}

接下來,和文章開頭一樣饭望,我們對集合進行了相關操作仗哨,可以看出使用泛型解決了我們之前遇到的所有問題:

  1. ① 處的代碼在編譯期間會出錯:由于聲明的是 Integer 類型的 List,顯然無法接收 String 類型的數(shù)據(jù)铅辞。
  2. 從虛擬 List 可以知道厌漂,取出元素時不需要顯示強轉,自然也不會在運行時拋出異常斟珊。

通過對泛型 List 的簡單運用桩卵,可以看出引入泛型后集合不失普適性,依然可以針對各種類型對象進行操作倍宾。同時,泛型為集合框架增加了編譯時類型安全性胜嗓,并避免了在使用過程中的強轉操作高职。

5. 總結

有關泛型的前生今世就介紹到這兒了。至此辞州,我們通過相關示例一步步引出了泛型怔锌,了解了泛型誕生前后在一些編碼場景下的差異。最后還通過實例簡單使用了泛型变过,但泛型的運用遠不止如此...

下一篇將進一步介紹泛型的各種運用場景埃元,掌握泛型的用武之地。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末媚狰,一起剝皮案震驚了整個濱河市岛杀,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌崭孤,老刑警劉巖类嗤,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件糊肠,死亡現(xiàn)場離奇詭異,居然都是意外死亡遗锣,警方通過查閱死者的電腦和手機货裹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來精偿,“玉大人弧圆,你說我怎么就攤上這事”恃剩” “怎么了搔预?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長拓轻。 經常有香客問我斯撮,道長,這世上最難降的妖魔是什么扶叉? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任勿锅,我火速辦了婚禮,結果婚禮上枣氧,老公的妹妹穿的比我還像新娘溢十。我一直安慰自己,他們只是感情好达吞,可當我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布张弛。 她就那樣靜靜地躺著,像睡著了一般酪劫。 火紅的嫁衣襯著肌膚如雪吞鸭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天覆糟,我揣著相機與錄音刻剥,去河邊找鬼。 笑死滩字,一個胖子當著我的面吹牛造虏,可吹牛的內容都是我干的。 我是一名探鬼主播麦箍,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼漓藕,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了挟裂?” 一聲冷哼從身側響起享钞,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎诀蓉,沒想到半個月后嫩与,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體寝姿,經...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡簸淀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年框产,在試婚紗的時候發(fā)現(xiàn)自己被綠了舶担。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片险掀。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡匕争,死狀恐怖狮荔,靈堂內的尸體忽然破棺而出盼樟,到底是詐尸還是另有隱情倒槐,我是刑警寧澤同窘,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布玄帕,位于F島的核電站,受9級特大地震影響想邦,放射性物質發(fā)生泄漏裤纹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一丧没、第九天 我趴在偏房一處隱蔽的房頂上張望鹰椒。 院中可真熱鬧,春花似錦呕童、人聲如沸漆际。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽奸汇。三九已至,卻和暖如春往声,著一層夾襖步出監(jiān)牢的瞬間擂找,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工浩销, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留贯涎,地道東北人。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓撼嗓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親欢唾。 傳聞我的和親對象是個殘疾皇子且警,可洞房花燭夜當晚...
    茶點故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內容