03.Java泛型擦除及帶來的問題和解決方案

一、什么是泛型擦除

  1. 偽泛型
    假如我們定義一個容器類(蘋果籃類)用來存放蘋果,則可以按照以下簡單實現(xiàn):
    class Fruit{}
    class Apple extends Fruit{}
    class BucketApple{
        private Apple apple;
        public Apple getApple() {return apple;}
        public void setApple(Apple apple) {this.apple = apple;}
    }
    
    那么如果現(xiàn)在又需要定一個容器類(香蕉籃類)用來存放香蕉,那么可以按以下簡單實現(xiàn):
    class Fruit{}
    class Banana extends Fruit{}
    class BucketBanana{
        private Banana banana;
        public Banana getBanana() {return banana;}
        public void setBanana(Bananabanana) {this.banana = banana;}
    }
    
    可以發(fā)現(xiàn)的是,類BucketBananaBucketApple有著相似之處,根據(jù)多態(tài)的原理我們可以直接使用Object類替代所有水果類(相當(dāng)于用Object做了Apple類和Banana類的泛型),那么蘋果籃和香蕉籃就可以簡化為一個水果籃:
    class Bucket{
        private Object obj;
        public Object getObject() {return obj;}
        public void setObject(Object obj) {this.obj = obj;}
    }
    
    實際中,Java本身也是利用Object實現(xiàn)的偽泛型,比如在代碼中定義List<String>List<String>等類型,在編譯后都會變成List,List底層存放的都是Object類型的對象,而不是定義時附加的類型信息String、Integer
    // ArrayList底層源碼,通過Object數(shù)組存放對象
    transient Object[] elementData;
    
  2. 泛型擦除
    還是用上面的List為例,實際存放的都是Object對象,定義List<Integer>時聲明的Integer只是用來檢查存入對象的類型。流程如下圖所示:
  • 無界通配符擦除
  • 有界通配符擦除
  • 擦除方法中類型定義的參數(shù)
  • 擦除接口中的泛型標(biāo)識

二、泛型擦除相關(guān)問題和注意事項

  1. 先檢查還是先編譯?類型校驗的根據(jù)是什么?引用傳遞問題
  • Q: 由上述可知編譯時會進(jìn)行泛型擦除,數(shù)據(jù)均轉(zhuǎn)換為Object存儲,那么定義集合如下,為什么在add(整數(shù))時還是提示錯誤?
    public static  void main(String[] args) {  
        ArrayList<String> list = new ArrayList<String>();  
        list.add("123");  
        list.add(123); //編譯錯誤  
     }
    
    A: Java編譯器是通過先檢查代碼中泛型的類型,然后再進(jìn)行類型擦除,再進(jìn)行編譯。
  • Q: 根據(jù)什么進(jìn)行類型檢查?引用中的限制類型還是初始化中的限制類型?
    A: 初始化時的new ArrayList()只是在內(nèi)存開辟了一個存儲空間,可存儲任意類型對象。泛型是根據(jù)引用對象中聲明的限制類型進(jìn)行校驗。
    public class Test {  
        public static void main(String[] args) {  
            ArrayList<String> list1 = new ArrayList();  
            list1.add("1"); //編譯通過  
            list1.add(1); //編譯錯誤  
            String str1 = list1.get(0); //返回類型就是String  
    
            ArrayList list2 = new ArrayList<String>();  
            list2.add("1"); //編譯通過  
            list2.add(1); //編譯通過  
            Object object = list2.get(0); //返回類型就是Object  
    
            new ArrayList<String>().add("11"); //編譯通過  
            new ArrayList<String>().add(22); //編譯錯誤  
            String str2 = new ArrayList<String>().get(0); //返回類型就是String  
        }  
    }  
    
  1. 自動類型轉(zhuǎn)換
  2. 類型擦除與多態(tài)的沖突和解決方法
  3. 泛型類型變量不能是基本數(shù)據(jù)類型
  4. 編譯時集合的instanceof
  5. 泛型在靜態(tài)方法和靜態(tài)類中的問題
    詳情參見鏈接
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容