java8 泛型 (generics)


title: java8教程-泛型(Generics)
date: 2016-06-28 14:04:35
tags:

  • java

泛型(已更新)

在任何繁瑣的(nontrivial)軟件項目中脖律,bug是家常便飯废膘。細心的規(guī)劃泳姐,編程和測試可以幫助減少bug的普遍性(pervasiveness),但是無論如何,無論在哪里徒役,bug總會伺機悄悄溜進(creep)你的代碼寞蚌,因為很明顯,新的特性會不斷的被引入麸折,并且你的代碼基數(shù)會不斷變大和復(fù)雜锡凝。

幸運的是,一些bug相比其它比較容易檢測垢啼。編譯時bug可以在早期被檢測到窜锯;你可以利用編譯器的錯誤信息查明是什么問題并且解決,就在那時芭析。然而锚扎,運行時bug會更加未預(yù)知,他們不會立即展示出來,不知道什么時候發(fā)生馁启,可能根本不在程序真正出現(xiàn)問題的點上驾孔。

泛型通過更多的在編譯時檢測bug為你的代碼增加了穩(wěn)定性。

為什么要用泛型

簡言之惯疙,泛型能夠使類型(類和接口)在定義類翠勉,接口和方法的時候參數(shù)化。非常像方法定義時用到的形式參數(shù)(formal parameters),類型參數(shù)提供了一種你可以通過不同的輸入來復(fù)用同一段代碼的方法霉颠。不同點是对碌,形式參數(shù)輸入的是,而類型參數(shù)輸入的是類型蒿偎。

使用泛型比非泛型有很多好處:

  • 編譯時更強大的類型檢測

Java編譯器對泛型應(yīng)用了強大的類型檢測朽们,如果代碼違反了類型安全就會報錯。修復(fù)編譯時錯誤比修復(fù)運行時錯誤更加容易诉位,因為運行時錯誤很難查找到骑脱。

  • 消除類型轉(zhuǎn)換(Elimination of casts)

以下代碼片段沒有泛型需要轉(zhuǎn)型:

List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);

當(dāng)我們重新用泛型編寫,代碼就不需要類型轉(zhuǎn)換了:

List<String> list = new ArrayList<String>();
list.add("hello");
String s = list.get(0);   // no cast
  • 使開發(fā)者實現(xiàn)泛型算法

通過泛型不从,開發(fā)者可以自己實現(xiàn)泛型算法惜姐,應(yīng)用到一系列的不同類型,可以自定義歹袁,并且類型安全坷衍,易讀枫耳。

泛型類型

泛型類型是泛型類或者接口被類型參數(shù)化。下面的Box類將被更改演示這個概念摊沉。

簡單的 Box 類

列舉一個簡單的非泛型 Box操作任意類型的object狐史。它只需要提供兩個方法:set,添加一個obejct到box说墨,get,獲取這個對象:

public class Box {
private Object object;

public void set(Object object) { this.object = object; }
public Object get() { return object; }
}

因為它的方法接收或返回一個對象骏全,你可以任意傳入,只要傳入的不是原始數(shù)據(jù)類型尼斧。我們沒有在編譯時辨別clas如何使用的姜贡。一邊可能替換一個 Integer到box,另一邊獲取的不是Integer類型棺棵,而可能傳入一個String類型楼咳,結(jié)果會導(dǎo)致運行時錯誤。

泛型版本的Box

泛型類的定義形式如下:

class name<T1, T2, ..., Tn> { /* ... */ }

類型參數(shù)部分被一對尖括號(<>)劃分律秃,緊跟類名爬橡,它指定了類型參數(shù)(也叫作類型變量)T1, T2棒动, ....,和Tn.

把原Box類更新為泛型類,你要通過把“public class Box”改變?yōu)椤皃ublic class Box<T>”創(chuàng)建一個類型聲明宾添。這會引入一個類型變量, T,你可以在類中任意地方使用船惨。通過這個改變,Box類就變?yōu)椋?/p>

/**
 * Generic version of the Box class.
 * @param <T> the type of the value being boxed
 */
public class Box<T> {
// T stands for "Type"
private T t;

public void set(T t) { this.t = t; }
public T get() { return t; }
}

你可以看到缕陕,所有Object出現(xiàn)的地方都被替換為T了粱锐。一個類型變量可以指定為任意非原始類型的類型:任意的類,任意的接口扛邑,任意的數(shù)組怜浅,甚至其他的類型變量。同樣的技術(shù)可以應(yīng)用到創(chuàng)建泛型接口上。

類型參數(shù)命名規(guī)則(Naming Conventions)

通過規(guī)則恶座,類型參數(shù)是單獨的搀暑,大寫字母。這個表示鮮明區(qū)別了你已知的變量命名規(guī)則跨琳,一個好的理由是:沒有這個規(guī)則自点,你將很難區(qū)分類型變量和原生類或接口名的區(qū)別。

最普遍使用的類型參數(shù)是:

  • E -Element(Java Collections框架大量使用)
  • K -Key
  • N -Number
  • T -Type
  • V -Value
  • S,U,V 等 -第二脉让,第三桂敛,第四個類型

你可以在JAVA SE API 看到這些名字的使用。

調(diào)用和實例化一個泛型類型

要在你的代碼引用泛型類 Box溅潜,你必須執(zhí)行 泛型類型調(diào)用术唬,把T替換成具體的值,比如Integer:

Box<Integer> integerBox;

你可以認(rèn)為泛型類型調(diào)用跟原生方法調(diào)用大致一樣滚澜,但是不是傳入一個參數(shù)到方法粗仓,而是傳入一個類型蠶食--這個情況下的Integer--給Box類本身。

Type ParameterType Argument術(shù)語(Terminology):
很多開發(fā)者交換使用這個兩個術(shù)語博秫,但是這兩個術(shù)語并不同潦牛。敲代碼時,
type argument 創(chuàng)建一個參數(shù)化類型挡育,因此巴碗,F(xiàn)oo< T>中的T是type parameter,F(xiàn)oo< String> f中的String是一個type argument即寒。

就想其他的變量定義橡淆,上面的代碼不會真正創(chuàng)建一個新的 Box對象。它只是聲明母赵,integerBox將持有一個“Box of Integer”的引用逸爵,用以讀取Box<Integer>.泛型類型的調(diào)用通常稱為參數(shù)化類型。

為了實例化這個類凹嘲,用new 關(guān)鍵字师倔,把<Integer>放在類名和括號之間。

Box<Integer> integerBox = new Box<Integer>();

The Diamond

在Java SE 7及以后版本周蹭,可以省去類型參數(shù)調(diào)用泛型類的構(gòu)造函數(shù)趋艘,用一個空的類型參數(shù)(<>),編譯器可以通過上下文決定,或推測type arguments凶朗,這個尖括號非正式得叫作diamond(鉆石瓷胧?這么奇葩),你可以這樣創(chuàng)建Box< Integer>的一個實例:

Box<Integer> integerBox = new Box<>();

要查看更多關(guān)于diamond 符號和類型推斷(inference),請看類型推斷棚愤。

多類型參數(shù)

正如前面提到的搓萧,泛型類可以有多個類型參數(shù)。比如泛型 OrderedPair 類,實現(xiàn)了泛型接口 Pair:

public interface Pair<K, V> {
public K getKey();
public V getValue();
}

public class OrderedPair<K, V> implements Pair<K, V> {

private K key;
private V value;

public OrderedPair(K key, V value) {
this.key = key;
this.value = value;
}

public K getKey()    { return key; }
public V getValue() { return value; }
}

下面的語句創(chuàng)建了兩個OrderedPair的實例:

Pair<String, Integer> p1 = new OrderedPair<String, Integer>("Even", 8);
Pair<String, String>  p2 = new OrderedPair<String, String>("hello", "world");

new OrderedPair<String,Integer>把K實例化為String瘸洛,V實例化為Integer揍移。因此OrderedPair的參數(shù)類型分別(respectively)是String和Integer。因為自動裝箱货矮,傳入String和int到類是有效的羊精。

參數(shù)化類型###

你也可以用一個參數(shù)化的類型(ie List< String>)替換(substitute)類型參數(shù)(K ,V),例如用OrderedPair< K,V>:

OrderedPair<String, Box<Integer>> p = new OrderedPair<>("primes", new Box<Integer>(...));

原類型(Raw Types)

原類型是指泛型類或泛型接口的名字沒有任何參數(shù)囚玫,比如喧锦,給出泛型類Box:

public class Box(T){
public void set(T t){
/* ...... */
}
}

你可以為形參T賦值一個真實的類型參數(shù)來創(chuàng)建一個參數(shù)化類型的 Box(T):

Box(Ingeter) intBox=new Box<>();

如果真實的類型參數(shù)被省略掉了,你就創(chuàng)建了一個原類型的Box<T>:

Box rawBox =new Box();

因此抓督,Box是Box<T>的原類型燃少。然而,非泛型類或非泛型接口沒有原類型铃在。
原類型出現(xiàn)在遺贈的代碼里是因為大量的API類(比如Collections類)在JDK5之前不是泛型類阵具。當(dāng)使用原類型的時候,你本質(zhì)上使用的是泛型之前的表現(xiàn)---Box ->Object.為了向后兼容定铜,賦值參數(shù)化類型給他的原類型是允許的:

Box<String> stringBox=new Box<>();
Box rawBox=stringBox;    //OK

但是如果你賦值一個原類型給一個參數(shù)化的類型阳液,你將得到警告:

Box rawBox=new Box(); //rawBox是Box<T>()的原類型
Box<Integer> intBox=rawBox;  //warning:unchecked conversion

當(dāng)你用原類型調(diào)用關(guān)聯(lián)的反省類型的泛型方法時,你也會得到警告:

Box<String> stringBox=new Box<>();
Box rawBox=stringBox;
rawBox.set(8);  //waring: unchecked invocation to set(T)

警告顯示原類型繞過泛型類型檢查揣炕,延遲捕獲不安全代碼到運行時帘皿。因此,你需要避免使用原類型畸陡。類型擦除部分會有更多關(guān)于Java編譯器如何使用原類型的內(nèi)容鹰溜。

Unchecked Error Messages

正如上面提到的,當(dāng)混合遺贈代碼和泛型代碼時丁恭,你可能會碰到跟下面相似的警告:

Note: Example.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

這發(fā)生在當(dāng)使用老的API操作原類型時曹动,例如如下代碼:

public class WarningDemo {
Box<Integer> bi;
bi=createBox();
}
static Box createBox(){
return new Box();
}

'unchecked'指的是編譯器沒有足夠的類型信息來執(zhí)行所有必要的類型檢查以保證類型安全。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末牲览,一起剝皮案震驚了整個濱河市墓陈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌第献,老刑警劉巖跛蛋,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異痊硕,居然都是意外死亡,警方通過查閱死者的電腦和手機押框,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門岔绸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事盒揉〗唬” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵刚盈,是天一觀的道長羡洛。 經(jīng)常有香客問我,道長藕漱,這世上最難降的妖魔是什么欲侮? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮肋联,結(jié)果婚禮上威蕉,老公的妹妹穿的比我還像新娘。我一直安慰自己橄仍,他們只是感情好韧涨,可當(dāng)我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著侮繁,像睡著了一般虑粥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上宪哩,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天娩贷,我揣著相機與錄音,去河邊找鬼斋射。 笑死育勺,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的罗岖。 我是一名探鬼主播涧至,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼桑包!你這毒婦竟也來了南蓬?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤哑了,失蹤者是張志新(化名)和其女友劉穎赘方,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體弱左,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡窄陡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了拆火。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片跳夭。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡涂圆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出币叹,到底是詐尸還是另有隱情润歉,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布颈抚,位于F島的核電站踩衩,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏贩汉。R本人自食惡果不足惜驱富,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望雾鬼。 院中可真熱鬧萌朱,春花似錦、人聲如沸策菜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽又憨。三九已至翠霍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蠢莺,已是汗流浹背寒匙。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留躏将,地道東北人锄弱。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像祸憋,于是被迫代替她去往敵國和親会宪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 開發(fā)人員在使用泛型的時候蚯窥,很容易根據(jù)自己的直覺而犯一些錯誤掸鹅。比如一個方法如果接收List作為形式參數(shù),那么如果嘗試...
    時待吾閱讀 1,042評論 0 3
  • 前言 人生苦多拦赠,快來 Kotlin 巍沙,快速學(xué)習(xí)Kotlin! 什么是Kotlin荷鼠? Kotlin 是種靜態(tài)類型編程...
    任半生囂狂閱讀 26,146評論 9 118
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理句携,服務(wù)發(fā)現(xiàn),斷路器允乐,智...
    卡卡羅2017閱讀 134,599評論 18 139
  • 《少有人走的路》第三篇筆記: 如果說“自律”是讓我們心智成熟务甥,解決自己人生痛苦的最重要的方法的話牡辽,那么自律的原動力...
    兩香蕉媽媽閱讀 584評論 1 2
  • 總覺得時間太真實了,是真實敞临,用彩色的筆在好看的紙上書寫,是不舍得但又獨自欣賞. 曾說的為金錢麸澜、名望到現(xiàn)在的只求平淡...
    閑澈閱讀 192評論 0 0