Java語法糖系列三:泛型與類型擦除

目錄:
Java語法糖系列一:可變長度參數(shù)和foreach循環(huán)
http://www.reibang.com/p/628568f94ef8

Java語法糖系列二:自動(dòng)裝箱/拆箱和條件編譯
http://www.reibang.com/p/946b3c4a5db6

Java語法糖系列三:泛型與類型擦除
http://www.reibang.com/p/4de08deb6ba4

Java語法糖系列四:枚舉類型
http://www.reibang.com/p/ae09363fe734

Java語法糖系列五:內(nèi)部類和閉包
http://www.reibang.com/p/f55b11a4cec2


前兩篇寫到可變長參數(shù)工育、foreach循環(huán)泪姨、自動(dòng)裝箱/拆箱和條件編譯奶栖,這篇討論下java的泛型與類型擦除棘脐。

泛型與類型擦除

泛型是JDK 1.5的一項(xiàng)新特性,它的本質(zhì)是參數(shù)化類型(Parameterized Type)的應(yīng)用棉姐,也就是說所操作的數(shù)據(jù)類型被指定為一個(gè)參數(shù)工碾。這種參數(shù)類型可以用在類、接口和方法的創(chuàng)建中踪央,分別稱為泛型類臀玄、泛型接口和泛型方法。

泛型思想早在C++語言的模板(Templates)中就開始生根發(fā)芽畅蹂,在Java語言處于還沒有出現(xiàn)泛型的版本時(shí)健无,只能通過Object是所有類型的父類和類型強(qiáng)制轉(zhuǎn)換兩個(gè)特點(diǎn)的配合來實(shí)現(xiàn)類型泛化。

例如在哈希表的存取中液斜,JDK 1.5之前使用HashMap的get()方法累贤,返回值就是一個(gè)Object對(duì)象叠穆,由于Java語言里面所有的類型都繼承于java.lang.Object,那Object轉(zhuǎn)型成任何對(duì)象都是有可能的臼膏。但是也因?yàn)橛袩o限的可能性硼被,就只有程序員和運(yùn)行期的虛擬機(jī)才知道這個(gè)Object到底是個(gè)什么類型的對(duì)象。在編譯期間渗磅,編譯器無法檢查這個(gè)Object的強(qiáng)制轉(zhuǎn)型是否成功嚷硫,如果僅僅依賴程序員去保障這項(xiàng)操作的正確性,許多ClassCastException的風(fēng)險(xiǎn)就會(huì)被轉(zhuǎn)嫁到程序運(yùn)行期之中始鱼。

泛型技術(shù)在C#和Java之中的使用方式看似相同仔掸,但實(shí)現(xiàn)上卻有著根本性的分歧,C#里面泛型無論在程序源碼中风响、編譯后的IL中(Intermediate Language嘉汰,中間語言,這時(shí)候泛型是一個(gè)占位符)或是運(yùn)行期的CLR中都是切實(shí)存在的状勤,List<int>與List<String>就是兩個(gè)不同的類型鞋怀,它們?cè)谙到y(tǒng)運(yùn)行期生成,有自己的虛方法表和類型數(shù)據(jù)持搜,這種實(shí)現(xiàn)稱為類型膨脹密似,基于這種方法實(shí)現(xiàn)的泛型被稱為真實(shí)泛型

Java語言中的泛型則不一樣葫盼,它只在程序源碼中存在残腌,在編譯后的字節(jié)碼文件中,就已經(jīng)被替換為原來的原生類型(Raw Type贫导,也稱為裸類型)了抛猫,并且在相應(yīng)的地方插入了強(qiáng)制轉(zhuǎn)型代碼。
先看個(gè)例子

public static void main(String[] args){
 List <Integer> listInt=new ArrayList <Integer>();
 List <String> listString=new ArrayList <String>();

 Map<String, String> map = new HashMap<String, String>();  
    map.put("AAA", "BBB");  
    map.put("CCC", "DDD");  
    System.out.print(map.get("AAA"));
}

編譯出來的代碼

public static void main(String[] paramArrayOfString)
   {
    ArrayList localArrayList1 = new ArrayList();
    ArrayList localArrayList2 = new ArrayList();

        HashMap localHashMap = new HashMap();
    localHashMap.put("AAA", "BBB");
    localHashMap.put("CCC", "DDD");
    System.out.print((String)localHashMap.get("AAA"));
  }

因此對(duì)于運(yùn)行期的Java語言來說孩灯,ArrayList <Integer>與ArrayList<String>編譯出來的代碼是一樣的闺金,所以說泛型技術(shù)實(shí)際上是Java語言的一顆語法糖,Java語言中的泛型實(shí)現(xiàn)方法稱為類型擦除峰档,基于這種方法實(shí)現(xiàn)的泛型被稱為偽泛型败匹。

泛型與重載

首先看看重載(Overloading)的定義:

  • Java的方法重載,就是在類中可以創(chuàng)建多個(gè)方法讥巡,它們具有相同的名字掀亩,但具有不同的參數(shù)和不同的定義。調(diào)用方法時(shí)通過傳遞給它們的不同參數(shù)個(gè)數(shù)和參數(shù)類型來決定具體使用哪個(gè)方法, 這就是多態(tài)性欢顷。
    
  • 重載的時(shí)候槽棍,方法名要一樣,但是參數(shù)類型和個(gè)數(shù)不一樣,返回值類型可以相同也可以不相同刹泄。
    
  • 無法以返回型別作為重載函數(shù)的區(qū)分標(biāo)準(zhǔn)外里。
    

上面也說了,泛型編譯出來的代碼是會(huì)把類型擦除的特石,所以如下的代碼是不能編譯的盅蝗,是因?yàn)閰?shù)List<Integer>和List<String>編譯之后都被擦除了,變成了一樣的原生類型List<E>姆蘸,擦除動(dòng)作導(dǎo)致這兩個(gè)方法的特征簽名變得一模一樣墩莫,或者說兩個(gè)一模一樣的方法不能共存在一個(gè)class文件里。

   public  void method(List<String> list) {  
      System.out.println(list.get(0));  
}  

public  void method(List<Integer> list) {  
    System.out.println(list.get(0));  
}  

那么如果加上返回類型呢逞敷?

public String method(List<String> list) {  
    System.out.println(list.get(0));  
    return "";  
}  

public int method(List<Integer> list) {  
    System.out.println(list.get(0));  
    return 1;  
}  

在eclipse 上仍然報(bào)錯(cuò)狂秦,參考重載定義的第三點(diǎn)無法以返回型別作為重載函數(shù)的區(qū)分標(biāo)準(zhǔn)

一點(diǎn)拓展

上面加上了返回類型的兩個(gè)方法,在eclipse上編譯不通過推捐,但在Javac編譯器中是可以編譯的裂问,編譯出來的代碼如下:

public String method(List<String> paramList)
  {
    System.out.println((String)paramList.get(0));
    return "";
  }

  public int method(List<Integer> paramList) {
        System.out.println(paramList.get(0));
        return 1;
  }

那是不是說明重載可以以返回類型作區(qū)分呢?不是的牛柒。因?yàn)橄褚韵逻@樣的代碼用javac也編譯不了

`public String method(String list) {  
    System.out.println(list);  
    return "";  
}  

public int method(String list) {  
    System.out.println(list);  
    return 1;  
}  `

網(wǎng)上找到一段引用:

在《Java虛擬機(jī)規(guī)范第二版》(JDK 1.5修改后的版本)的“§4.4.4 Signatures”章節(jié)及《Java語言規(guī)范第三版》的“§8.4.2 Method Signature”章節(jié)中分別都定義了字節(jié)碼層面的方法特征簽名堪簿,以及Java代碼層面的方法特征簽名,特征簽名最重要的任務(wù)就是作為方法獨(dú)一無二不可重復(fù)的ID皮壁,在Java代碼中的方法特征簽名只包括了方法名稱椭更、參數(shù)順序及參數(shù)類型,而在字節(jié)碼中的特征簽名還包括方法返回值及受查異常表蛾魄。

根據(jù)上面的例子說明:由于List<String>和List<Integer>擦除后是同一個(gè)類型虑瀑,只能添加兩個(gè)并不需要實(shí)際使用到的返回值才能完成重載。這是否是一種引入泛型后的折中的解決方案呢滴须?

最后舌狗,通過反射依然能獲取到參數(shù)化的類型,說明擦除法所謂的擦除扔水,僅僅是對(duì)方法的Code屬性中的字節(jié)碼進(jìn)行擦除痛侍,實(shí)際上元數(shù)據(jù)中還是保留了泛型信息。
測(cè)試如圖:

反射獲取參數(shù)化類型.jpg

參考:http://icyfenix.iteye.com/blog/1021949?page=2#comments

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末铭污,一起剝皮案震驚了整個(gè)濱河市恋日,隨后出現(xiàn)的幾起案子膀篮,更是在濱河造成了極大的恐慌嘹狞,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件誓竿,死亡現(xiàn)場(chǎng)離奇詭異磅网,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)筷屡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門涧偷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來簸喂,“玉大人,你說我怎么就攤上這事燎潮∮黯” “怎么了?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵确封,是天一觀的道長除呵。 經(jīng)常有香客問我,道長爪喘,這世上最難降的妖魔是什么颜曾? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮秉剑,結(jié)果婚禮上泛豪,老公的妹妹穿的比我還像新娘。我一直安慰自己侦鹏,他們只是感情好诡曙,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著种柑,像睡著了一般岗仑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上聚请,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天荠雕,我揣著相機(jī)與錄音,去河邊找鬼驶赏。 笑死炸卑,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的煤傍。 我是一名探鬼主播盖文,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼蚯姆!你這毒婦竟也來了五续?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤龄恋,失蹤者是張志新(化名)和其女友劉穎疙驾,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體郭毕,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡它碎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扳肛。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡傻挂,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出挖息,到底是詐尸還是另有隱情金拒,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布套腹,位于F島的核電站殖蚕,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏沉迹。R本人自食惡果不足惜睦疫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望鞭呕。 院中可真熱鬧蛤育,春花似錦、人聲如沸葫松。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽腋么。三九已至咕娄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間珊擂,已是汗流浹背圣勒。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留摧扇,地道東北人圣贸。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像扛稽,于是被迫代替她去往敵國和親吁峻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

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

  • 前面在张,由于對(duì)泛型擦除的思考用含,引出了對(duì)Java-Type體系的學(xué)習(xí)。本篇帮匾,就讓我們繼續(xù)對(duì)“泛型”進(jìn)行研究: JDK1...
    賈博巖閱讀 5,139評(píng)論 3 28
  • 前言 人生苦多啄骇,快來 Kotlin ,快速學(xué)習(xí)Kotlin辟狈! 什么是Kotlin肠缔? Kotlin 是種靜態(tài)類型編程...
    任半生囂狂閱讀 26,163評(píng)論 9 118
  • 開發(fā)人員在使用泛型的時(shí)候,很容易根據(jù)自己的直覺而犯一些錯(cuò)誤哼转。比如一個(gè)方法如果接收List作為形式參數(shù)明未,那么如果嘗試...
    時(shí)待吾閱讀 1,045評(píng)論 0 3
  • UIWebView 一、概覽(Overview) 1.支持的文件格式(Supported File Formats...
    ShannonChenCHN閱讀 1,272評(píng)論 0 1
  • 從小一起長大的菇?jīng)雒魈煲鰟e人的新娘了壹蔓,替她開心趟妥,即使婚前有糾結(jié),有煩惱佣蓉,既然選擇了披摄,就要嫁給幸福~ 很...
    傻丫頭的西瓜閱讀 196評(píng)論 0 1