1. 程式人生 > >Java 泛型小結

Java 泛型小結

gen 字母 row edt 替換 復用 pear 生產 pad

1、什麽是泛型?

泛型(Generics )是把類型參數化,運用於類、接口、方法中,可以通過執行泛型類型調用 分配一個類型,將用分配的具體類型替換泛型類型。然後,所分配的類型將用於限制容器內使用的值,這樣就無需進行類型轉換,還可以在編譯時提供更強的類型檢查。

2、泛型有什麽用?

泛型主要有兩個好處:

(1)消除顯示的強制類型轉換,提高代碼復用

(2)提供更強的類型檢查,避免運行時的ClassCastException

3、泛型的使用

類型參數(又稱類型變量)用作占位符,指示在運行時為類分配類型。根據需要,可能有一個或多個類型參數,並且可以用於整個類。根據慣例,類型參數是單個大寫字母,該字母用於指示所定義的參數類型。下面列出每個用例的標準類型參數:

E:元素

K:鍵

N:數字

T:類型

V:值

S、U、V 等:多參數情況中的第 2、3、4 個類型

? 表示不確定的java類型(無限制通配符類型)

4、有界泛型

<? extends T>:是指 “ 上界通配符 (Upper Bounds Wildcards) ”

<? super T>:是指 “ 下界通配符 (Lower Bounds Wildcards) ”

—這裏有個坑

如 List<? extends T> 大家以為元素為 T以及其所有子類的對象 的List。其實不是。元素類型 僅指T的某一個不確定的子類,是單一的一個不確定類,沒有具體哪個類。因此不能插入一個不確定的。

List<? super T> 大家以為元素為 T以及其父類的對象 的List。其實不是,元素類型 僅指T的某一個不確定的父類,是單一的一個不確定類(只確定是T的父類),沒有具體哪個類。

因此:

不能往List<? extends T>中插入任何類型的對象。唯一可以保證的是,你可以從中讀取到T或者T的子類。

可以往List<? super T>中插入T或者T子類的對象,但不可以插入T父類的對象。可以讀取到Object或者Object子類的對象(你並不知道具體的子類是什麽)

總結一下:

如果頻繁支持讀取數據,不要求寫數據,使用<? extends T>。即生產者 使用 <? extends T>;

如果頻繁支持寫入數據,不特別要求讀數據,使用<? super T>。即消費者 使用 <? super T>;

如果都需要支持,使用<T>。

5、類型擦除

Java的泛型在編譯期間,所有的泛型信息都會被擦除掉。

  • Class c1 = new ArrayList<Integer>().getClass();

    Class c2 = new ArrayList<Long>().getClass();

    System.out.println(c1 == c2);

    這就是 Java 泛型的類型擦除造成的,因為不管是 ArrayList<Integer> 還是 ArrayList<Long>,在編譯時都會被編譯器擦除成了 ArrayList。Java 之所以要避免在創建泛型實例時而創建新的類,從而避免運行時的過度消耗。

  • 6、泛型類型信息

    那麽,如果我們確實某些場景,如HTTP或RPC或jackson需要獲取泛型進行序列化反序列化的時候,需要獲取泛型類型信息。

    可以參照如下:

  • //獲取運行時的泛型類型信息
    public class Test2 {
     
        static class ParameterizedTypeReference<T> {
            protected final Type type;
     
            public ParameterizedTypeReference() {
                Type superClass = this.getClass().getGenericSuperclass();
                //if (superClass instanceof Class) {
        // throw new IllegalArgumentException(
    //"Internal error: TypeReference constructed without actual type information");
                //  } else {
                    this.type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
                //}
            }
     
            public Type getType() {
                return type;
            }
        }
     
        public static void main(String[] args) {
    // System.out.println(new ParameterizedTypeReference<String>().getType());
    // java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
    // 此處會輸出報錯,因此ParameterizedTypeReference 應不能直接實例化,可以考慮加abstract
     
            System.out.println(new ParameterizedTypeReference<String>() { }.getType());
    // ParameterizedTypeReference 的匿名內部類,可以觸發super(),
    //即 ParameterizedTypeReference()的構造器邏輯,正常運行
        }
     
    }
    

      

  • 註意一個關鍵點:

    可以通過定義類的方式(通常為匿名內部類,因為我們創建這個類只是為了獲得泛型信息)在運行時獲得泛型參數。

Java 泛型小結