java泛型

開發(fā)人員在使用泛型的時(shí)候,很容易根據(jù)自己的直覺而犯一些錯(cuò)誤,比如一個(gè)方法如果接收List<Object>作為形式參數(shù),那么如果嘗試將一個(gè)List<String>的對(duì)象作為實(shí)際參數(shù)傳進(jìn)去,卻發(fā)現(xiàn)無(wú)法通過編譯驹溃。雖然從直覺來說,Object是String的父類儒搭,這個(gè)類型轉(zhuǎn)換應(yīng)該是合理的吠架。 ** 但是實(shí)際上這會(huì)產(chǎn)生隱含的類型轉(zhuǎn)換問題,因此編譯器直接就禁止這樣的行為 ** 搂鲫。

類型擦除

java中的泛型基本上都在編譯器這個(gè)層次來實(shí)現(xiàn)的傍药,** 在生成的java字節(jié)代碼中是不包含泛型中的數(shù)據(jù)類型的。使用泛型的時(shí)候加上的類型參數(shù)魂仍,會(huì)被編譯器在編譯的時(shí)候去掉拐辽,這個(gè)過程稱為類型擦除 ** 。如在代碼中定義的List<Object>和List<String>等類型擦酌,在編譯之后都會(huì)變成List俱诸。 ** JVM看到的只是list,而由泛型附加的類型信息對(duì)JVM來說是不可以見的 ** 赊舶。java編譯器會(huì)在編譯時(shí)盡可能發(fā)現(xiàn)可能出錯(cuò)的地方睁搭,但是仍然無(wú)法避免在運(yùn)行時(shí)刻出現(xiàn)類型轉(zhuǎn)換異常的情況。
很多泛型的奇怪特性都與這個(gè)類型擦除的存在有關(guān)笼平,包括:

  • ** 泛型類并沒有自己獨(dú)有的class類對(duì)象 ** 园骆。比如并不存在List<String>.class或是List<Integer>.class,而只有List.class。
  • ** 靜態(tài)變量是被泛型類的所有實(shí)例所共享的 ** 寓调。對(duì)于聲明為MyClass<T>的類锌唾,訪問其中的靜態(tài)變量的方法仍然是MyClass.myStaticVar。不管是通過 new MyClass<String>還是new MyClass<Integer>創(chuàng)建的對(duì)象,都是共享一個(gè)靜態(tài)變量晌涕。
  • ** 泛型的類型參數(shù)不能用在java異常處理的catch語(yǔ)句中滋捶,因?yàn)楫惓L幚硎怯蒍VM在運(yùn)行時(shí)刻來進(jìn)行的 ** 。由于類型信息被擦除余黎,JVM是無(wú)法區(qū)分兩個(gè)異常類型MyException<String>和MyException<Integer>的重窟。對(duì)于JVM來說,它們都的MyException類型的惧财。也就是無(wú)法執(zhí)行與異常對(duì)應(yīng)的catch語(yǔ)句亲族。

類型擦除的基本過程也比較簡(jiǎn)單,首先是找到用來替換類型參數(shù)的具體類可缚。這個(gè)具體類一般是Object。如果指定了類型參數(shù)的上界的話斋枢,則使用這個(gè)上界帘靡。把代碼中的類型參數(shù)都替換成具體的類。同時(shí)去掉出現(xiàn)的類型聲明瓤帚,即去掉<>的內(nèi)容描姚。比如 T get()方法聲明就變成了Object get();List<String>就變成了List。接下來就可能需要生成一些橋接方法(bridge method)戈次。這是由于擦除了類型之后的類可能缺少某些必須的方法轩勘。比如考慮下面的代碼:

 class MyString implements Comparable<String> {
     public int compareTo(String str) {        
         return 0;    
     }
 }

當(dāng)類型信息被擦除之后,上述類的聲明變成了class MyString implements Comparable怯邪。但是這樣的話绊寻,類MyString就會(huì)有編譯錯(cuò)誤,因?yàn)闆]有實(shí)現(xiàn)接口Comparable聲明的int compareTo(Object)方法悬秉。這個(gè)時(shí)候就由編譯器來動(dòng)態(tài)生成這個(gè)方法澄步。

通配符

在使用泛型類的時(shí)候,既可以指定一個(gè)具體的類型和泌,如List<String>就聲明了具體的類型是String;也可以用通配符?來表示未知類型村缸,如List<?>就聲明了List中包含的元素類型是未知的。通配符所代表的其實(shí)是一組類型武氓,但具體的類型是未知的梯皿。List<?>所聲明的就是所有類型都是可以的。 ** 但是List <?> 并不等同于List<Object>县恕。List<Object>實(shí)際上確定了List中包含的是Object及子類东羹,在使用的時(shí)候都可以通過Object來進(jìn)行引用。而List<?>則其中包含的元素類型是不確定 **弱睦。其中可能包含的是String,也可能是Integer百姓。如果它包含了String的話,往里面添加Integer類型的元素就是錯(cuò)誤的况木, ** 正因?yàn)轭愋臀粗萋#筒荒芡ㄟ^new ArrayList<?>()方法來創(chuàng)建一個(gè)新的ArrayList對(duì)象旬迹,因?yàn)榫幾g器無(wú)法知道具體的類型是什么。但是對(duì)于List<?>中的元素總是可以用Object來引用的求类,因?yàn)殡m然類型未知奔垦,但肯定是Object及其子類 ** ∈考慮下面的代碼:

public void wildcard(List<?> list) {
    list.add(1);//編譯錯(cuò)誤
}  

如上所示椿猎,試圖對(duì)一個(gè)帶通配符的泛型類進(jìn)行操作的時(shí)候,總是會(huì)出現(xiàn)編譯錯(cuò)誤寿弱,其它原因在于通配符所表示的類型是未知的犯眠。
因?yàn)閷?duì)于List<?>中的元素只能用Object來引用,在有些情況下不是很方便症革。在這些情況下筐咧,可以使用上下界來限制未知類型的范圍。如List <? extends Number>說明List中可能包含的元素類型是Number及其子類噪矛。而List <? super Number>則說明List中包含的是Number及其父類量蕊。當(dāng)引入了上界之后,在使用類型的時(shí)候就可以使用上界類中定義的方法艇挨。

類型系統(tǒng)

在java中残炮,大家比較熟悉的是通過繼承機(jī)制而產(chǎn)生的類型體系結(jié)構(gòu)。比如String繼承自O(shè)bject缩滨。根據(jù)Listov替換原則势就,子類是可以替換父類的。當(dāng)需要Object類的引用的時(shí)候脉漏,如果傳入一個(gè)String對(duì)象是沒有任何問題的蛋勺。但是反過來的話,即用父類的引用替換子類引用的時(shí)候鸠删,就需要進(jìn)行強(qiáng)制類型轉(zhuǎn)換抱完。編譯器并不能保證運(yùn)行時(shí)刻這種轉(zhuǎn)換一定是合法的。** 這種自動(dòng)的子類替換父類的類型轉(zhuǎn)換機(jī)制刃泡,對(duì)于數(shù)組也是適用的巧娱。String[]可以替換Object[] ** 。但是泛型的引入烘贴,對(duì)于這個(gè)類型系統(tǒng)產(chǎn)生了一定的影響禁添。 ** 正始前面提到的list是不是能替換掉List的。引入泛型之后的類型系統(tǒng)增加了兩個(gè)維度:一個(gè)是類型參數(shù)自身的繼承體系結(jié)構(gòu)桨踪,另外一個(gè)是泛型類或接口自身的繼承體系結(jié)構(gòu)老翘。第一個(gè)指的是對(duì)于List<String>和List<Object>這樣的情況。類型String是繼承自O(shè)bject的。而第二種指的是List接口繼承自Collection接口铺峭。對(duì)于這個(gè)類型的系統(tǒng)墓怀,有如下的一些規(guī)則: **

  • 相同類型參數(shù)的泛型的關(guān)系取決于泛型類自身的繼承體系結(jié)構(gòu)。即List<String>是Collection<Sting>的子類型卫键,List<String>可以替換Collection<String>傀履。這種情況也適用于帶有上下界的類型聲明。
  • 當(dāng)泛型類的類型聲明中使用了通配符的時(shí)候莉炉,其子類型可以在兩個(gè)維度上分別展開钓账。如對(duì)Collection <? extends Number>來說絮宁,其子類型可以在Collection這個(gè)維度上展開梆暮,即List <? extends Number>和Set<? extends Number>等;也可以在Number這個(gè)層次上展開,即Collection<Double>和Collection<Integer>等绍昂。如此循環(huán)下去惕蹄,ArrayList<Long>和HashSet<Double>等也都算是Collection <? extends Number>的子類型。
  • 如果泛型類中包含多個(gè)類型參數(shù)治专,則對(duì)于每個(gè)類型參數(shù)分別應(yīng)用上面的規(guī)則。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末遭顶,一起剝皮案震驚了整個(gè)濱河市张峰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌棒旗,老刑警劉巖喘批,帶你破解...
    沈念sama閱讀 212,294評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異铣揉,居然都是意外死亡饶深,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,493評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門逛拱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來敌厘,“玉大人,你說我怎么就攤上這事朽合【懔剑” “怎么了?”我有些...
    開封第一講書人閱讀 157,790評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵曹步,是天一觀的道長(zhǎng)宪彩。 經(jīng)常有香客問我,道長(zhǎng)讲婚,這世上最難降的妖魔是什么尿孔? 我笑而不...
    開封第一講書人閱讀 56,595評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上活合,老公的妹妹穿的比我還像新娘雏婶。我一直安慰自己,他們只是感情好芜辕,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,718評(píng)論 6 386
  • 文/花漫 我一把揭開白布尚骄。 她就那樣靜靜地躺著,像睡著了一般侵续。 火紅的嫁衣襯著肌膚如雪倔丈。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,906評(píng)論 1 290
  • 那天状蜗,我揣著相機(jī)與錄音需五,去河邊找鬼。 笑死轧坎,一個(gè)胖子當(dāng)著我的面吹牛宏邮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播缸血,決...
    沈念sama閱讀 39,053評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼蜜氨,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了捎泻?” 一聲冷哼從身側(cè)響起飒炎,我...
    開封第一講書人閱讀 37,797評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎笆豁,沒想到半個(gè)月后郎汪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,250評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡闯狱,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,570評(píng)論 2 327
  • 正文 我和宋清朗相戀三年煞赢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片哄孤。...
    茶點(diǎn)故事閱讀 38,711評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡照筑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出瘦陈,到底是詐尸還是另有隱情朦肘,我是刑警寧澤,帶...
    沈念sama閱讀 34,388評(píng)論 4 332
  • 正文 年R本政府宣布双饥,位于F島的核電站媒抠,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏咏花。R本人自食惡果不足惜趴生,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,018評(píng)論 3 316
  • 文/蒙蒙 一阀趴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧苍匆,春花似錦刘急、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,796評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至检碗,卻和暖如春据块,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背折剃。 一陣腳步聲響...
    開封第一講書人閱讀 32,023評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工另假, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人怕犁。 一個(gè)月前我還...
    沈念sama閱讀 46,461評(píng)論 2 360
  • 正文 我出身青樓边篮,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親奏甫。 傳聞我的和親對(duì)象是個(gè)殘疾皇子戈轿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,595評(píng)論 2 350

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

  • 開發(fā)人員在使用泛型的時(shí)候,很容易根據(jù)自己的直覺而犯一些錯(cuò)誤阵子。比如一個(gè)方法如果接收List作為形式參數(shù)思杯,那么如果嘗試...
    時(shí)待吾閱讀 1,045評(píng)論 0 3
  • 泛型的好處 使用泛型的好處我覺得有兩點(diǎn):1:類型安全 2:減少類型強(qiáng)轉(zhuǎn) 下面通過一個(gè)例子說明: 假設(shè)有一個(gè)Tes...
    德彪閱讀 1,118評(píng)論 0 0
  • 2.6 Java泛型詳解 Java泛型是JDK5中引入的一個(gè)新特性,允許在定義類和接口的時(shí)候使用類型參數(shù)(type...
    jianhuih閱讀 678評(píng)論 0 3
  • 前面款筑,由于對(duì)泛型擦除的思考,引出了對(duì)Java-Type體系的學(xué)習(xí)腾么。本篇奈梳,就讓我們繼續(xù)對(duì)“泛型”進(jìn)行研究: JDK1...
    賈博巖閱讀 5,139評(píng)論 3 28
  • 晚餐 包子皮吃一半
    陳敬Chris閱讀 665評(píng)論 0 51