初探Java類型擦除

本篇博客主要介紹了Java類型擦除的定義,詳細的介紹了類型擦除在Java中所出現的場景惦界。

1. 什么是類型擦除

為了讓你們快速的對類型擦除有一個印象衬潦,首先舉一個很簡單也很經典的例子蜡励。

// 指定泛型為StringList list1 =newArrayList<>();// 指定泛型為IntegerList list2 =newArrayList<>();System.out.println(list1.getClass() == list2.getClass());// true

上面的判斷結果是?true?。代表了兩個傳入了不同泛型的List最終都編譯成了ArrayList锦担,成為了同一種類型俭识,原來的泛型參數String和Integer被擦除掉了。這就是類型擦除的一個典型的例子洞渔。

而如果我們說到類型擦除為什么會出現套媚,我們就必須要了解泛型。

2. 泛型

2.1. 泛型的定義

隨著2004年9月30日磁椒,工程代號為Tiger的JDK 1.5發(fā)布堤瘤,泛型從此與大家見面。JDK 1.5在Java語法的易用性上作出了非常大的改進浆熔。除了泛型本辐,同版本加入的還有自動裝箱、動態(tài)注解、枚舉慎皱、可變長參數老虫、foreach循環(huán)等等。

而在1.5之前的版本中茫多,為了讓Java的類具有通用性祈匙,參數類型和返回類型通常都設置為Object,可見天揖,如果需要不用的類型夺欲,就需要在相應的地方,對其進行強制轉換今膊,程序才可以正常運行些阅,十分麻煩,稍不注意就會出錯万细。

泛型的本質就是參數化類型。也就是纸泄,將一個數據類型指定為參數赖钞。引入泛型有什么好處呢?

泛型可以將JDK 1.5之前在運行時才能發(fā)現的錯誤聘裁,提前到編譯期雪营。也就是說,泛型提供了編譯時類型安全的檢測機制衡便。例如献起,一個變量本來是Integer類型,我們在代碼中設置成了String镣陕,沒有使用泛型的時候只有在代碼運行到這了谴餐,才會報錯。

而引入泛型之后就不會出現這個問題呆抑。這是因為通過泛型可以知道該參數的規(guī)定類型岂嗓,然后在編譯時,判斷其類型是否符合規(guī)定類型鹊碍。

泛型總共有三種使用方法厌殉,分別使用于類、方法和接口侈咕。

3. 泛型的使用方法

3.1 泛型類

3.1.1 定義泛型類

簡單的泛型類可以定義為如下公罕。

publicclassGeneric{? ? Tdata;publicGeneric(Tdata) {? ? ? ? setData(data);? ? }publicT getData() {returndata;? ? }publicvoid setData(Tdata) {this.data=data;? ? }}

其中的T代表參數類型,代表任何類型耀销。當然楼眷,并不是一定要寫成T,這只是大家約定俗成的習慣而已。有了上述的泛型類之后我們就可以像如下的方式使用了摩桶。

3.1.2 使用泛型類

// 假設有這樣一個具體的類publicclassHello {privateInteger id;privateStringname;privateInteger age;privateStringemail;}// 使用泛型類Hello hello =newHello();Generic result =newGeneric<>();resule.setData(hello);// 通過泛型類獲取數據Hello data = result.getData();

當然如果泛型類不傳入指定的類型的話桥状,泛型類中的方法或者成員變量定義的類型可以為任意類型,如果打印?result.getClass()?的話硝清,會得到?Generic?辅斟。

3.2. 泛型方法

3.2.1 定義泛型方法

首先我們看一下不帶返回值的泛型方法,可以定義為如下結構芦拿。

// 定義不帶返回值的泛型方法publicvoidgenericMethod(T field){? ? System.out.println(field.getClass().toString());}// 定義帶返回值的泛型方法privateTgenericWithReturnMethod(T field){? ? System.out.println(field.getClass().toString());returnfield;}

3.2.2 調用泛型方法

// 調用不帶返回值泛型方法genericMethod("This is string");// class java.lang.StringgenericMethod(56L);// class java.lang.Long// 調用帶返回值的泛型方法Stringtest = genericWithReturnMethod("TEST");// TEST class java.lang.String

帶返回值的方法中士飒,T就是當前函數的返回類型。

3.3. 泛型接口

泛型接口定義如下

publicinterfacegenericInterface{}

使用的方法與泛型類類似蔗崎,這里就不再贅述酵幕。

4. 泛型通配符

什么是泛型通配符?官方一點的解釋是

Typeofunknown.

也就是無限定的通配符缓苛,可以代表任意類型芳撒。用法也有三種,<?>未桥,<? extends T>和<? super T>笔刹。

既然已經有了T這樣的代表任意類型的通配符,為什么還需要這樣一個無限定的通配符呢冬耿?是因為其主要解決的問題是泛型繼承帶來的問題舌菜。

4.1. 泛型的繼承問題

首先來看一個例子

ListintegerList = new ArrayList<>();ListnumberList = integerList;

我們知道,?Integer?是繼承自?Number?類的亦镶。

publicfinalclassIntegerextendsNumberimplementsComparable

那么上述的代碼能夠通過編譯嗎日月?肯定是不行的。Integer繼承自Number不代表List

4.2. 通配符的應用場景

在其他函數中缤骨,例如JavaScript中爱咬,一個函數的參數可以是任意的類型,而不需要進行任意的類型轉換绊起,所以這樣的函數在某些應用場景下台颠,就會具有很強的通用性。

而在Java這種強類型語言中勒庄,一個函數的參數類型是固定不變的串前。那如果想要在Java中實現類似于JavaScript那樣的通用函數該怎么辦呢?這也就是為什么我們需要泛型的通配符实蔽。

假設我們有很多動物的類, 例如Dog, Pig和Cat三個類荡碾,我們需要有一個通用的函數來計算動物列表中的所有動物的腿的總數,如果在Java中局装,要怎么做呢坛吁?

可能會有人說劳殖,用泛型啊,泛型不就是解決這個問題的嗎拨脉?泛型必須指定一個特定的類型哆姻。正式因為泛型解決不了...才提出了泛型的通配符。

4.3. 無界通配符

無界通配符就是???玫膀∶В看到這你可能會問,這不是跟T一樣嗎帖旨?為啥還要搞個???箕昭。他們主要區(qū)別在于,T主要用于聲明一個泛型類或者方法解阅,?主要用于使用泛型類和泛型方法落竹。下面舉個簡單的例子。

// 定義打印任何類型列表的函數publicstaticvoid printList(List<?>list) {for(Object elem:list) {? ? ? ? System.out.print(elem +" ");? ? }}// 調用上述函數List intList = Arrays.asList(1,2,3);List stringList = Arrays.asList("one","two","three");printList(li);// 1 2 3 printList(ls);// one two three

上述函數的目的是打印任何類型的列表货抄∈稣伲可以看到在函數內部,并沒有關心List中的泛型到底是什么類型的蟹地,你可以將<?>理解為只提供了一個只讀的功能积暖,它去除了增加具體元素的能力,只保留與具體類型無關的功能锈津。從上述的例子可以看出呀酸,它只關心元素的數量以及其是否為空凉蜂,除此之外不關心任何事琼梆。

再反觀T偎行,上面我們也列舉了如何定義泛型的方法以及如果調用泛型方法涂籽。泛型方法內部是要去關心具體類型的椭坚,而不僅僅是數量和不為空這么簡單桩引。

4.4. 上界通配符<? extends T>

既然???可以代表任何類型吮播,那么extends又是干嘛的呢纲辽?

假設有這樣一個需求祈纯,我們只允許某一些特定的類型可以調用我們的函數(例如糠爬,所有的Animal類以及其派生類)轧邪,但是目前使用???刽脖,所有的類型都可以調用函數,無法滿足我們的需求忌愚。

privateintcountLength(List< ? extends Animal>list){...}

使用了上界通配符來完成這個公共函數之后曲管,就可以使用如下的方式來調用它了。

List pigs =newArrayList<>();List dogs =newArrayList<>();List cats =newArrayList<>();// 假裝寫入了數據intsum =0;sum += countLength(pigs);sum += countLength(dogs);sum += countLength(cats);

看完了例子硕糊,我們就可以簡單的得出一個結論院水。上界通配符就是一個可以處理任何特定類型以及是該特定類型的派生類的通配符腊徙。

可能會有人看的有點懵逼,我結合上面的例子檬某,再簡單的用人話解釋一下:上界通配符就是一個啥動物都能放的盒子撬腾。

4.5. 下界通配符<? super Animal>

上面我們聊了上界通配符,它將未知的類型限制為特定類型或者該特定的類型的子類型(也就是上面討論過的動物以及一切動物的子類)恢恼。而下界通配符則將未知的類型限制為特定類型或者該特定的類型的超類型民傻,也就是超類或者基類。

在上述的上界通配符中厅瞎,我們舉了一個例子饰潜。寫了一個可以處理任何動物類以及是動物類的派生類的函數。而現在我們要寫一個函數和簸,用來處理任何是Integer以及是Integer的超類的函數彭雾。

publicstaticvoidaddNumbers(Listlist){for(inti =1; i <=10; i++) {list.add(i);? ? }}

5. 類型擦除

簡單的了解了泛型的幾種簡單的使用方法之后,我們回到本篇博客的主題上來——類型擦除锁保。泛型雖然有上述所列出的一些好處薯酝,但是泛型的生命周期只限于編譯階段。

本文最開始的給出的樣例就是一個典型的例子爽柒。在經過編譯之后會采取去泛型化的措施吴菠,編譯的過程中,在檢測了泛型的結果之后會將泛型的相關信息進行擦除操作浩村。就像文章最開始提到的例子一樣做葵,我們使用上面定義好的Generic泛型類來舉個簡單的例子。

Generic generic =newGeneric<>("Hello");Field[] fs = generic.getClass().getDeclaredFields();for(Field f : fs) {? ? System.out.println("type: "+ f.getType().getName());// type: java.lang.Object}

getDeclaredFields?是反射中的方法心墅,可以獲取當前類已經聲明的各種字段酿矢,包括public,protected以及private怎燥。

可以看到我們傳入的泛型String已經被擦除了瘫筐,取而代之的是Object。那之前的String和Integer的泛型信息去哪兒了呢铐姚?可能這個時候你會靈光一閃策肝,那是不是所有的泛型在被擦除之后都會變成Object呢?別著急隐绵,繼續(xù)往下看之众。

當我們在泛型上面使用了上界通配符以后,會有什么情況發(fā)生呢依许?我們將Generic類改成如下形式棺禾。

publicclassGeneric{? ? Tdata;publicGeneric(Tdata) {? ? ? ? setData(data);? ? }publicT getData() {returndata;? ? }publicvoid setData(Tdata) {this.data=data;? ? }}

然后再次使用反射來查看泛型擦除之后類型。這次控制臺會輸出?type: java.lang.String?悍手×蹦溃可以看到袍患,如果我們給泛型類制定了上限,泛型擦除之后就會被替換成類型的上限竣付。而如果沒有指定诡延,就會統(tǒng)一的被替換成Object。相應的古胆,泛型類中定義的方法的類型也是如此肆良。

我自己是一個從事了6年的Java全棧工程師,最近整理了一套適合2019年學習的Java\大數據資料逸绎,從基礎的Java惹恃、大數據面向對象到進階的框架知識都有整理哦,可以來我的主頁免費領取哦棺牧。

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末巫糙,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子颊乘,更是在濱河造成了極大的恐慌参淹,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件乏悄,死亡現場離奇詭異浙值,居然都是意外死亡,警方通過查閱死者的電腦和手機檩小,發(fā)現死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門开呐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人规求,你說我怎么就攤上這事筐付。” “怎么了颓哮?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵家妆,是天一觀的道長鸵荠。 經常有香客問我冕茅,道長,這世上最難降的妖魔是什么蛹找? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任姨伤,我火速辦了婚禮,結果婚禮上庸疾,老公的妹妹穿的比我還像新娘乍楚。我一直安慰自己,他們只是感情好届慈,可當我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布徒溪。 她就那樣靜靜地躺著忿偷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪臊泌。 梳的紋絲不亂的頭發(fā)上鲤桥,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天,我揣著相機與錄音渠概,去河邊找鬼茶凳。 笑死,一個胖子當著我的面吹牛播揪,可吹牛的內容都是我干的贮喧。 我是一名探鬼主播,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼猪狈,長吁一口氣:“原來是場噩夢啊……” “哼箱沦!你這毒婦竟也來了?” 一聲冷哼從身側響起雇庙,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤饱普,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后状共,有當地人在樹林里發(fā)現了一具尸體套耕,經...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年峡继,在試婚紗的時候發(fā)現自己被綠了冯袍。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡碾牌,死狀恐怖康愤,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情舶吗,我是刑警寧澤征冷,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站誓琼,受9級特大地震影響检激,放射性物質發(fā)生泄漏。R本人自食惡果不足惜腹侣,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一叔收、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧傲隶,春花似錦饺律、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽脖卖。三九已至,卻和暖如春巧颈,著一層夾襖步出監(jiān)牢的瞬間胚嘲,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工洛二, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留馋劈,地道東北人。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓晾嘶,卻偏偏與公主長得像妓雾,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子垒迂,可洞房花燭夜當晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內容

  • 本篇博客主要介紹了Java類型擦除的定義械姻,詳細的介紹了類型擦除在Java中所出現的場景。 1. 什么是類型擦除 為...
    編程小世界閱讀 670評論 0 0
  • “泛型”這個術語的意思是:"適用于許多許多的類型”机断。如何做到這一點呢楷拳,正是通過解耦類或方法與所使用的類型之間的約束...
    王偵閱讀 1,133評論 0 0
  • 泛型,一個孤獨的守門者吏奸。 大家可能會有疑問欢揖,我為什么叫做泛型是一個守門者。這其實是我個人的看法而已奋蔚,我的意思是說泛...
    傳奇內服號閱讀 404評論 0 0
  • 泛型她混,一個孤獨的守門者。 大家可能會有疑問泊碑,我為什么叫做泛型是一個守門者坤按。這其實是我個人的看法而已,我的意思是說泛...
    程序員BUG閱讀 361評論 0 0
  • 一覺睡醒馒过,七點臭脓。渾身舒坦,本以為十二點了腹忽。真的是有生物鐘的来累,就是自己給自己一個暗示。有點浮夸留凭,但是就是這么真實佃扼。我...
    thomas_郭閱讀 272評論 0 0