這是我在抖音二面的時候自我感受沒有答好的一題。由於個人中心只是圍繞在了T
被Object
替換的問題上了,並無去講解他會帶來的問題。java
其實咱們很常見這個問題,你甚至常常用,只是沒有去注意罷了,可是很不碰巧這樣的問題就容易被面試官抓住。下面先來看一段代碼吧。面試
List list = new ArrayList();
List listString = new ArrayList<String>();
List listInteger = new ArrayList<Integer>();
複製代碼
這幾段代碼簡單、粗暴、又帶有很濃厚的熟悉感是吧。那我接下來要把一個數字1
插入到這三段不同的代碼中了。dom
做爲讀者的你可能如今已經黑人問號了????你確定有不少疑問,這明顯不同啊,怎麼可能。函數
public class Main {
public static void main(String[] args) {
List list = new ArrayList();
List listString = new ArrayList<String>();
List listInteger = new ArrayList<Integer>();
try {
list.getClass().getMethod("add", Object.class).invoke(list, 1);
listString.getClass().getMethod("add", Object.class).invoke(listString, 1);
// 給不服氣的讀者們的測試之處,你能夠改爲字符串來嘗試。
listInteger.getClass().getMethod("add", Object.class).invoke(listInteger, 1);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("list size:" + list.size());
System.out.println("listString size:" + listString.size());
System.out.println("listInteger size:" + listInteger.size());
}
}
複製代碼
上述的就是泛型擦除的一種表現了,可是爲了更好的理解,固然要更深刻了是吧。雖然List
很大,但卻也不是不能看看。post
兩個關鍵點,來驗證一下:學習
// 先來看看畫了一個大餅的List
// 可以過很清楚的看到泛型E
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
// 第一個關鍵點
// 還沒開始就出問題的存儲類型
// 難道不該該也是一個泛型E?
transient Object[] elementData;
public E get(int index) {
rangeCheck(index);
return elementData(index); // 1---->
}
// 由1直接調用的函數
// 第二個關鍵點,強制轉化得來的數據
E elementData(int index) {
return (E) elementData[index];
}
}
複製代碼
我想,其實你也可以懂了,這個所謂的泛型T
最後會被轉化爲一個Object
,最後又經過強制轉化來進行一個轉變。從這裏咱們也就可以知道爲何咱們的數據從前面過來的時候,String
類型數據可以直接被Integer
進行接收了。測試
(1) 強制類型轉化this
這個問題的結果咱們已經在上述文章中說起到了,經過反射的方式去進行插入的時候,咱們的數據就會發生錯誤。spa
若是咱們在一個List<Integer>
中在不知情的狀況下插入了一個String
類型的數值,那這種重大錯誤,咱們該找誰去說呢。操作系統
(2)引用傳遞問題
上面的問題中,咱們已經說過了T
將在後期被轉義成Object
,那咱們對引用也進行一個轉化,是否行得通呢?
List<String> listObject = new ArrayList<Object>();
List<Object> listObject = new ArrayList<String>();
複製代碼
若是你這樣寫,在咱們的檢查階段,會報錯。可是從邏輯意義上來講,其實你真的有錯嗎?
假設說咱們的第一種方案是正確的,那麼其實就是將一堆Object
數據存入,而後再由上面所說的強制轉化通常,轉化成String
類型,聽起來徹底ok,由於在List
中原本存儲數據的方式就是Object
。但實際上是會出現ClassCastException
的問題,由於Object
是萬物的基類,可是強轉是爲子類向父類準備的措施。
再來假設說咱們的第二種方案是正確的,這個時候,根據上方的數據String
存入,可是有什麼意義存在呢?最後都仍是要成Object
的,你還不如就直接是Object
。
其實很簡單,若是看過一些公開課想來就見過這樣的用法。
public class Part<T extends Parent> {
private T val;
public T getVal() {
return val;
}
public void setVal(T val) {
this.val = val;
}
}
複製代碼
相比較於以前的Part
而言,他多了<T extends Parent>
的語句,其實這就是將基類從新規劃的操做,就算被編譯,虛擬機也會知道將數據轉化爲Parent
而不是直接用Object
來直接進行替代。
以上就是個人學習成果,若是有什麼我沒有思考到的地方或是文章內存在錯誤,歡迎與我分享。
相關文章推薦: