Java 不能實現(xiàn)真正泛型的原因是什么哭廉?

破案了懂算!Java 不能實現(xiàn)真正泛型的原因

本文已同步至 GitHub 《教妹學 Java》專欄顶燕,風趣幽默,通俗易懂饿敲,對 Java 初學者親切友善(目前已更新 49 篇)妻导,內(nèi)容包括 Java 語法、Java 集合框架怀各、Java 并發(fā)編程栗竖、Java 虛擬機等核心知識點,歡迎 star渠啤。

GitHub 開源地址:https://github.com/itwanger/jmx-java

在線閱讀地址:https://itwanger.gitee.io/jmx-java/#/

簡單來回顧一下類型擦除狐肢,看下面這段代碼。

public class Cmower {
    public static void method(ArrayList<String> list) {
        System.out.println("Arraylist<String> list");
    }

    public static void method(ArrayList<Date> list) {
        System.out.println("Arraylist<Date> list");
    }
}

在淺層的意識上沥曹,我們會認為 ArrayList<String> listArrayList<Date> list 是兩種不同的類型份名,因為 String 和 Date 是不同的類碟联。

但由于類型擦除的原因,以上代碼是不會編譯通過的——編譯器會提示一個錯誤:

'method(ArrayList<String>)' clashes with 'method(ArrayList<Date>)'; both methods have same erasure

也就是說僵腺,兩個 method() 方法經(jīng)過類型擦除后的方法簽名是完全相同的鲤孵,Java 是不允許這樣做的。

也就是說辰如,按照我們的假設(shè):如果 Java 能夠?qū)崿F(xiàn)真正意義上的泛型普监,兩個 method() 方法是可以同時存在的,就好像方法重載一樣琉兜。

public class Cmower {
    public static void method(String list) {
    }

    public static void method(Date list) {
    }
}

為什么 Java 不能實現(xiàn)真正意義上的泛型呢凯正?背后的原因是什么?

第一豌蟋,兼容性

Java 在 2004 年已經(jīng)積累了較為豐富的生態(tài)廊散,如果把現(xiàn)有的類修改為泛型類,需要讓所有的用戶重新修改源代碼并且編譯梧疲,這就會導致 Java 1.4 之前打下的江山可能會完全覆滅允睹。

想象一下,你的代碼原來運行的好好的幌氮,就因為 JDK 的升級缭受,導致所有的源代碼都無法編譯通過并且無法運行,是不是會非常痛苦该互?

類型擦除就完美實現(xiàn)了兼容性米者,Java 1.5 之后的類可以使用泛型,而 Java 1.4 之前沒有使用泛型的類也可以保留慢洋,并且不用做任何修改就能在新版本的 Java 虛擬機上運行塘雳。

老用戶不受影響,新用戶可以自由地選擇使用泛型普筹,可謂一舉兩得败明。

第二,不是“實現(xiàn)不了”

這部分內(nèi)容參考自 R大@RednaxelaFX

Pizza太防,1996 年的實驗語言妻顶,在 Java 的基礎(chǔ)上擴展了泛型。

Pizza 教程地址:http://pizzacompiler.sourceforge.net/doc/tutorial.html

這里插一下 Java 的版本歷史蜒车,大家好有一個時間線上的觀念讳嘱。

  • 1995年5月23日,Java語言誕生
  • 1996年1月酿愧,JDK1.0 誕生
  • 1997年2月18日沥潭,JDK1.1發(fā)布
  • 1998年2月,JDK1.1被下載超過2,000,000次
  • 2000年5月8日嬉挡,JDK1.3發(fā)布
  • 2000年5月29日钝鸽,JDK1.4發(fā)布
  • 2004年9月30日18:00 PM汇恤,J2SE1.5 發(fā)布

也就是說,Pizza 在 JDK 1.0 的版本上就實現(xiàn)了“真正意義上的”泛型拔恰,我引過來兩段例子因谎,大家一看就明白了。

首先是 StoreSomething颜懊,一個泛型類财岔,標識符是大寫字母 A 而不是我們熟悉的大寫字母 T。

class StoreSomething<A> {
     A something;

     StoreSomething(A something) {
         this.something = something;
     }

     void set(A something) {
         this.something = something;
     }

     A get() {
         return something;
     }
}

這個 A 呢河爹,可以是任何合法的 Java 類型:

StoreSomething<String> a = new StoreSomething("I'm a string!");
StoreSomething<int> b = new StoreSomething(17+4);

b.set(9);

int i = b.get();
String s = a.get();

對吧匠璧?這就是我們想要的“真正意義上的泛型”,A 不僅僅可以是引用類型 String昌抠,還可以是基本數(shù)據(jù)類型患朱。要知道鲁僚,Java 的泛型不允許是基本數(shù)據(jù)類型炊苫,只能是包裝器類型。

除此之外冰沙,Pizza 的泛型還可以直接使用 new 關(guān)鍵字進行聲明侨艾,并且 Pizza 編譯器會從構(gòu)造方法的參數(shù)上推斷出具體的對象類型,究竟是 String 還是 int拓挥。要知道唠梨,Java 的泛型因為類型擦除的原因,程序員是無法知道一個 ArrayList 究竟是 ArrayList<String> 還是 ArrayList<Integer> 的侥啤。

ArrayList<Integer> ints = new ArrayList<Integer>();
ArrayList<String> strs = new ArrayList<String>();

System.out.println(ints.getClass());
System.out.println(strs.getClass());

輸出結(jié)果:

class java.util.ArrayList
class java.util.ArrayList

都是 ArrayList 而已当叭。

那 Pizza 這種“真正意義上的泛型”為什么沒有被 Java 采納呢?這是大家都很關(guān)心的問題盖灸。

事實上蚁鳖,Java 的核心開發(fā)組對 Pizza 的泛型設(shè)計非常感興趣,并且與 Pizza 的設(shè)計者 Martin 和 Phil 取得了聯(lián)系赁炎,新合作了一個項目 Generic Java醉箕,爭取在 Java 中添加泛型支持,但不引入 Pizza 的其他功能徙垫,比如說函數(shù)式編程讥裤。

這里再補充一點維基百科上的資料,Martin Odersky 是一名德國計算機科學家姻报,他和其他人一起設(shè)計了 Scala 編程語言己英,以及 Generic Java(還有之前的 Pizza),他實現(xiàn)的 Generic Java 編譯器成為了 Java 編譯器 javac 的基礎(chǔ)吴旋。

站在馬后炮的思維來看损肛,Pizza 的泛型設(shè)計和函數(shù)式編程非常具有歷史前瞻性寒亥。然而 Java 的核心開發(fā)組在當時似乎并不想把函數(shù)式編程引入到 Java 中。

以至于 Java 在 1.4 之前仍然是不支持泛型的荧关,為什么 Java 1.5 的時候又突然支持泛型了呢溉奕?

當然是到了不支持不行的時候了。

沒有泛型之前忍啤,我們可以這樣寫代碼:

ArrayList list = new ArrayList();
list.add("沉默王二");
list.add(new Date());

不管是 String 類型加勤,還是 Date 類型,都可以一股腦塞進 ArrayList 當中同波,這看起來似乎很方便鳄梅,但取的時候就悲劇了。

String s = list.get(1);

這樣取行嗎未檩?

不行戴尸。

還得加上強制轉(zhuǎn)換。

String s = (String) list.get(1);

但我們知道冤狡,這行代碼在運行的時候必然會出錯:

Exception in thread "main" java.lang.ClassCastException: java.util.Date cannot be cast to java.lang.String

這就又回到“兼容性”的問題了孙蒙。

Java 語言和其他編程語言不一樣,有著沉重的歷史包袱悲雳,1.5 之前已經(jīng)有大量的程序部署在生產(chǎn)環(huán)境下了挎峦,這時候如果一刀切,原來沒有使用泛型的代碼直接扼殺了合瓢,后果不堪想象坦胶。

Java 一直以來都強調(diào)兼容性,我認為這也是 Java 之所以能被廣泛使用的主要原因之一晴楔,開發(fā)者不必擔心 Java 版本升級的問題顿苇,一個在 JDK 1.4 上可以跑的代碼,放在 JDK 1.5 上仍然可以跑税弃。

這里必須得說明一點纪岁,J2SE1.5 的發(fā)布,是 Java 語言發(fā)展史上的重要里程碑钙皮,為了表示該版本的重要性蜂科,J2SE1.5 也正式更名為 Java SE 5.0,往后去就是 Java SE 6.0短条,Java SE 7.0导匣。。茸时。贡定。

但 Java 并不支持高版本 JDK 編譯生成的字節(jié)碼文件在低版本的 JRE(Java 運行時環(huán)境)上跑。

針對泛型可都,兼容性具體表現(xiàn)在什么地方呢缓待?

ArrayList<Integer> ints = new ArrayList<Integer>();
ArrayList<String> strs = new ArrayList<String>();
ArrayList list;
list = ints;
list = strs;

表現(xiàn)在上面這段代碼必須得能夠編譯運行蚓耽。怎么辦呢?

就只能搞類型擦除了旋炒!

真所謂“表面上一套步悠,背后玩另外一套”呀!

編譯前進行泛型檢測瘫镇,ArrayList<Integer> 只能放 Integer鼎兽,ArrayList<String> 只能放 String,取的時候就不用擔心類型強轉(zhuǎn)出錯了铣除。

但編譯后的字節(jié)碼文件里谚咬,是沒有泛型的,放的都是 Object尚粘。

Java 神奇就神奇在這择卦,表面上萬物皆對象,但為了性能上的考量郎嫁,又存在 int秉继、double 這種原始類型,但原始類型又沒辦法和 Object 兼容行剂,于是我們就只能寫 ArrayList<Integer> 這樣很占用內(nèi)存空間的代碼秕噪。

這恐怕也是 Java 泛型被吐槽的原因之一了钳降。

一個好消息是 Valhalla 項目正在努力解決這些因為泛型擦除帶來的歷史遺留問題厚宰。

Project Valhalla:正在進行當中的 OpenJDK 項目,計劃給未來的 Java 添加改進的泛型支持遂填。

源碼地址:http://openjdk.java.net/projects/valhalla/

讓我們拭目以待吧铲觉!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市吓坚,隨后出現(xiàn)的幾起案子撵幽,更是在濱河造成了極大的恐慌,老刑警劉巖礁击,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盐杂,死亡現(xiàn)場離奇詭異,居然都是意外死亡哆窿,警方通過查閱死者的電腦和手機链烈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來挚躯,“玉大人强衡,你說我怎么就攤上這事÷肜螅” “怎么了漩勤?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵感挥,是天一觀的道長。 經(jīng)常有香客問我越败,道長触幼,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任究飞,我火速辦了婚禮域蜗,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘噪猾。我一直安慰自己霉祸,他們只是感情好,可當我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布袱蜡。 她就那樣靜靜地躺著丝蹭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪坪蚁。 梳的紋絲不亂的頭發(fā)上奔穿,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天,我揣著相機與錄音敏晤,去河邊找鬼贱田。 笑死,一個胖子當著我的面吹牛嘴脾,可吹牛的內(nèi)容都是我干的男摧。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼译打,長吁一口氣:“原來是場噩夢啊……” “哼耗拓!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起奏司,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤乔询,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后韵洋,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體竿刁,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年搪缨,在試婚紗的時候發(fā)現(xiàn)自己被綠了食拜。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡勉吻,死狀恐怖监婶,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤惑惶,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布煮盼,位于F島的核電站,受9級特大地震影響带污,放射性物質(zhì)發(fā)生泄漏僵控。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一鱼冀、第九天 我趴在偏房一處隱蔽的房頂上張望报破。 院中可真熱鬧,春花似錦千绪、人聲如沸充易。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽盹靴。三九已至,卻和暖如春瑞妇,著一層夾襖步出監(jiān)牢的瞬間稿静,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工辕狰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留改备,地道東北人。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓蔓倍,卻偏偏與公主長得像悬钳,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子柬脸,可洞房花燭夜當晚...
    茶點故事閱讀 44,843評論 2 354

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

  • 在泛型出現(xiàn)以前他去,類和方法只能接受具體的類型。假設(shè)我們自己實現(xiàn)一個簡單的ArrayList倒堕,用來持有類A的實例,它可...
    辛美爾閱讀 3,045評論 0 5
  • 在Java技術(shù)下談“編譯期”而沒有具體上下文語境的話爆价,其實是一句很含糊的表述垦巴,因為它可能是指一個前端編譯器(叫“編...
    tracy_668閱讀 731評論 0 3
  • Java泛型食用筆記(二) -- 類型擦除 在使用別人已經(jīng)創(chuàng)建的泛型類時,你可能會感覺到泛型給你帶來的諸多方便铭段。但...
    不智魚閱讀 553評論 0 1
  • 什么是泛型骤宣? Java泛型(generics)是JDK5中引入的一個新特性,泛型提供了編譯時的類型安全監(jiān)測機制序愚;該...
    終于曉得閱讀 333評論 0 0
  • 我是黑夜里大雨紛飛的人啊 1 “又到一年六月憔披,有人笑有人哭,有人歡樂有人憂愁,有人驚喜有人失落芬膝,有的覺得收獲滿滿有...
    陌忘宇閱讀 8,536評論 28 53