java泛型總結學習

什麼是泛型?

  • 泛型 , 即 “參數化類型” . 提到參數 , 最熟悉的就是定義方法時有形參 , 然後調用方法傳遞實參 . 那麼參數化類型如何理解? 就是將類型由原來具體的類型參數化 , 類似於方法中的變量參數 , 此時類型也定義成參數形式 , 然後在調用的時候傳入具體的類型 .
    下面採用泛型後, 當通過List<’String> , 就相當於給List傳遞了參數類型是String, 限定了集合中只能含有String類型元素 , 集合保存了這個類型, 當add(“123”) , 無需進行類型轉換 , 因爲編譯器明確可以傳遞String類型 . 當然如果傳入Integer類型是不會通過編譯的 .
    這裏寫圖片描述
  • java中一種類型 , 被參數化的類型 , 也就是可以通過參數傳遞類型 . 帶參數的類型就是泛型 , 被參數化的類型 .

泛型擦除:

java泛型概念的提出 , 導致泛型只是作用於代碼的編譯階段 , 在編譯過程中 , 對於正確的檢驗泛型結果後 , 會將泛型的相關信息擦除 , 也就是說 , 成功編譯過後的class文件是不包含任何的泛型信息的 . 泛型信息不會進入運行時階段 .
泛型其實只是在編譯器實現的,而虛擬機並不認識泛型類型,所以要在虛擬機中將泛型類型進行擦除 , 也就是說編譯階段使用泛型 , 運行階段取消泛型 ,
擦除是將泛型類型由父類代替的 , 如String變成Object , 其實使用的時候還是帶着強制的類型轉換 , 只不過這是比較安全的轉換 , 因爲在編譯階段已經確保數據的一致性 .


驗證泛型只在編譯器有效:

所有反射操作是在運行期, 既然是true, 說明編譯之後, 程序會採用去泛型化措施 ,
編譯過程中, 正確檢驗泛型結果後, 會將泛型的相關信息擦除, 並且在對象進入和離開方法的邊界處添加類型檢查和類型轉換方法, 也就是說成功編譯後的class方法是不包含任何泛型信息的

public static void main(String[] args) {
        List<String> stringList = new ArrayList<>();
        List commonList = new ArrayList();
        // 說明運行期字節碼文件是相同的
        System.out.println(stringList.getClass() == commonList.getClass()); // true
    }

利用反射跳過泛型檢查:

public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("zhangsan");
        // 利用泛型進行添加
        try{
            Class clazz = list.getClass();
            Method method = clazz.getMethod("add", Object.class);
            // 添加Integer類型數據, 因爲反射是在運行期, 說明泛型的檢驗是在編譯期
            method.invoke(list, 100);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(list);
    }

泛型通配符:

對於任意(引用)類型 T,ArrayList<’?> 類型是ArrayList<’T> 的超類型(類似原始類型 ArrayList 和根類型 Object)。
但是ArrayList<’Object>並不是ArrayList<’String>的超類型.

這裏寫圖片描述


泛型上下限:

T super Number,那麼T就是Number父類級別的類型,
T extends Number 的話,Number是T的父級別類型.
這裏寫圖片描述


應用:

  • 獲取下面test方法的形參中泛型的參數類型.
public class Test {

    /**
     * 三個參數, 只有兩個是泛型類型參數
     * @param map
     * @param list
     * @param str
     */
    public void test(Map<String,Integer> map, List<String> list, String str) {
        System.out.println("hello world");
    }

    public static void main(String[] args) {
        try {
            Method method = Test.class.getMethod
                    ("test", Map.class, List.class, String.class);
            // 獲取方法的形參
            Type[] paramterTypes = method.getGenericParameterTypes();
            // 遍歷所有類型, 判斷是否是泛型的類型
            for(Type type : paramterTypes) {
                if(type instanceof ParameterizedType) {
                    Type[] ts = ((ParameterizedType)type).getActualTypeArguments();
                    for(Type t : ts) {
                        // 打印結果:String  Integer  String
                        System.out.println(((Class)t).getSimpleName());
                    }
                }

            }
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
}
  • 比如在三大框架整合中, 我們需要將所有增刪改查操作方法放在父類中, UserService操作的實體類型是User, DepartService操作類型是Depart . 那麼如何讓父類BaseService獲得到子類操作的實體類型(Hibernate中增刪改查需要獲取實體類型名稱) ?
    父類:
public class BaseService<T> {

    private Class<T> clazz;

    @SuppressWarnings("unchecked")
    public BaseService() {
        // 獲取子類的class對象
        Class c = (Class<T>) this.getClass();
        // 獲取子類的extend類型
        Type type = c.getGenericSuperclass();
        // 判斷是否是泛型
        if(type instanceof ParameterizedType) {
            // 獲取泛型的參數類型(默認獲取第一個)
            Type t = ((ParameterizedType)type).getActualTypeArguments()[0];
            clazz = (Class<T>) t;
            System.out.println(clazz.getSimpleName());
        }
    }
}

UserService:

public class UserService extends BaseService<User> {

    public static void main(String[] args) {
        new UserService(); // User
    }
}

DepartService:

public class DepartService extends BaseService<Depart> {

    public static void main(String[] args) {
        new DepartService(); // Depart
    }
}

泛型好處 :
(1) 類型安全
通過泛型定義的變量類型限制, 編譯器可以很有效的提高java程序的類型安全 .
(2) 消除強制類型轉換
消除強制類型轉換, 使得代碼更加可讀, 並減少出錯的機會, 所有的強制轉換都是自動

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章