面試官問我:「泛型擦除是什麼,會帶來什麼問題?」

前言

這是我在抖音二面的時候自我感受沒有答好的一題。由於個人中心只是圍繞在了TObject替換的問題上了,並無去講解他會帶來的問題。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

兩個關鍵點,來驗證一下:學習

  1. 數據存儲類型
  2. 數據獲取
// 先來看看畫了一個大餅的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來直接進行替代。

以上就是個人學習成果,若是有什麼我沒有思考到的地方或是文章內存在錯誤,歡迎與我分享。


相關文章推薦:

【從零衝擊音視頻開發】音視頻開發必備知識基礎

答應我,收好這份釘釘和抖音的面經,真的很重要!!!

操做系統中的三大經典同步問題,你如何復現?

相關文章
相關標籤/搜索