點擊上方「IT爛筆頭」,選擇「置頂公衆號」javascript
第一時間獲取 IT 技術乾貨!java
閱讀文本大概須要 6 分鐘。android
1web
Java是怎麼實現泛型的?不錯,類型擦除。Java編譯器將源碼編譯成字節碼的時候會將你在源碼中聲明的類型進行擦除,好比:微信
List<String> list = new ArrayList<String>();
在編譯後,字節碼中只有List,運行在JVM中也是同樣的,那你可能會有疑問,既然將類型擦除了,那爲何我聲明的泛型爲String類型時,不能往裏add一個整型的數據呢?這是由於編譯器在編譯前會進行類型檢查,類型不一致會直接編譯報錯。
通常做爲初級工程師知道這些就算合格了。app
咱們往深一層研究下,難道咱們必定不能往聲明泛型爲String的list中增長一個整型元素嗎?聰明的同窗可能想到了,既然是在編譯前檢查類型的,編譯後又將類型擦除了,那我是否是能夠在運行時經過反射將整型數字add進去?不錯!確實是能夠的。編輯器
List<Long> list = new ArrayList<Long>();list.add(100L);list.getClass().getMethod("add", Object.class).invoke(list, "abc");
// 而後咱們將list打印出來for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i));}
打印出來確實是能夠的,可是我能改爲下面這樣嗎?ui
List<Long> list = new ArrayList<Long>();list.add(100L);list.getClass().getMethod("add", Object.class).invoke(list, "abc");
Long a = 0L;// 而後咱們將list打印出來for (int i = 0; i < list.size(); i++) { a += list.get(i);}
固然不能啦,固然若是你想的比較仔細,會發現一個很好玩的現象。對於上面這個list中第二個元素爲"abc",那咱們分別以不一樣的類型來賦值:url
String s = "";Long a = 0L;s = list.get(1);a = list.get(1);
不管你是用String仍是Long類型來接收都會報錯,你用String來接收會報"Long cannot be converted to String"。你用Long類型來賦值會報"String cannot be cast to java.lang.Long",你是否是感到迷茫了,其實這一切都是由於類型擦除,對於不能使用String類型來接收是由於編譯器會作檢查,由於list聲明的泛型是Long類型的,而你使用String類型來賦值顯然編譯器會報錯,第二種你使用Long類型來接收,編譯器固然會認爲是合法的,可是在運行的時候,list中的第二個實際值是String,強轉成Long固然會報錯了。這一切的根源是你使用反射向list中放入了一個和聲明不一樣類型的數據。正常咱們通常也不會這麼作啦,這裏只是驗證一下這個機制而已。spa
好了,解釋了這麼多類型擦除的機制,那Java使用類型擦除來實現泛型有什麼好處呢?
一、第一點咱們將如此多的泛型在編譯時擦除了,那麼在運行時顯然能夠省很多的內存空間嘛。
二、第二點不得不說下兼容性,Java是在1.5版本推出的泛型,那1.5以前存在大量的線上代碼沒有泛型的,總不能捨棄吧,因此編譯擦除後和沒有泛型不是同樣嗎,這就兼容了以前更老的Java版本。
若是到這裏你基本上都會的話,我以爲徹底具備中級工程師的能力了。
2
類 型 擦 除 帶 來 的 問 題
任何設計都會有本身的優勢和缺點,在瞭解類型擦除的優勢以後,咱們也要剖析下類型擦除存在的現實問題:
一、不能使用基本數據類型
對於基本數據類型咱們必須使用它的裝箱類,那在咱們使用過程當中必然會平凡的涉及到拆箱和裝箱的操做,這一定帶來必定的資源開銷,因此谷歌在針對key是int類型的狀況下,使用SparseArray來代替HashMap。
二、不能用來方法的重載
爲何呢?舉個例子:
如上圖所示,在不一樣的泛型做爲參數時,編譯器編譯時進行類型擦除,那參數不就同樣了嗎?那還談什麼重載呢!而C#沒有進行類型擦除,因此編譯完後是帶有泛型的類型的,因此能夠看成是重載的。
三、泛型類型不能看成真實的類型使用
上圖中展現了5種使用方式,除了第四種Java能正常使用,其餘Java都不能使用,而C#徹底沒問題。
四、靜態方法沒法引用類的泛型類型
Java中的泛型是類實例化的時候才能肯定泛型的準確類型,而靜態方法是不須要類實例化就能調用的,顯然不能使用。
五、類型強轉的開銷
在Java1.5以前的版本,如上圖所示,必需要進行強轉才能使用本身想要的類型。
那Java1.5及之後的版本呢?
有興趣的能夠看看ArrayList的源碼,它的get方法仍是會作強轉的。
若是到這裏你都知道,說明你對泛型以及類型擦除了解的仍是比較系統,徹底具有高工的潛質。
推薦閱讀:
THANDKS
- End -
本文分享自微信公衆號 - IT爛筆頭(nj_android)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。