Java的泛型機制

Java的泛型機制

泛型是 Java 從 JDK5 開始引入的新特性,本質上是參數化類型,即所操作的數據類型被指定為一個參數。這意味著編寫的程式碼可以被很多不同類型的對象所重用。

1. 泛型的使用方式

1.1 泛型類

用下面的語法可以定義一個泛型類:

class C< T, E, ...>{
    private T t;
    ...
}

常用的泛型標識有 T、E、K、V。

用下面的語法可以創建一個泛型對象:

C<具體的數據類型> c = new C<>();

泛型類有以下注意事項:

  • 如果沒有指定具體的數據類型,操作類型是 Object。
  • 泛型的類型參數只能是類類型,不能是基本數據類型。
  • 泛型類型在邏輯上看作多個不同類型,但實際上是相同類型。

用下面的語法可以從泛型類派生子類:

// 子類也是泛型類,要和父類的泛型類型保持一致。但可以添加更多類型。
class Child<T> extends Father<T>
class Child<T, E, K> extends Father<T>  
// 子類不是泛型類,父類要明確一個泛型的數據類型
class Child extends Father<String>

1.2 泛型介面

定義方式類似於泛型類。

當需要用一個類實現泛型介面時:

  • 如果實現類不是泛型類,介面要明確數據類型。
  • 如果實現類是泛型類,實現類和介面的泛型類型要一致,但也可以增加更多。

1.3 泛型方法

用下面的語法可以定義一個泛型方法:

public <T, E, ...> void f(){
    ...
}

泛型類的類型由構造對象時決定,泛型方法的類型由調用方法時決定。

1.4 類型通配符

我們用?作為類型通配符,代表具體的類型實參。

使用extends語句可以代表類型通配符的上限:

類/介面<? extends 實參類型>

要求該泛型的類型,只能是實參類型或者實參類型的子類類型。

使用super語句可以代表類型通配符的下限:

類/介面<? super 實參類型>

帶有超類型限定的通配符可以向泛型對象寫入,帶有子類型限定的通配符可以從泛型對象讀取。

2. 類型擦除式泛型

Java 的泛型實現方式是類型擦除的偽泛型。在 Java 中,泛型只在程式源碼中存在,編譯後的位元組碼文件中泛型全部被擦除,替換為原來的裸類型,並在相應的位置插入了強制轉型程式碼。

假設我們有下面這段程式碼:

public static void main(String[] args){
	Map<String, String> map = new HashMap<String, String>();
    map.put("hello", "你好");
    System.out.pirntln(map.get("hello"));
}

這裡向泛型類型為<Stirng, String>的 map 內插入了一個鍵值對,又從中將值取出。如果我們先將這段 Java 程式碼編譯為 Class 文件,又反編譯 Class 文件,實際上會得到以下程式碼:

public static void main(String[] args){
	Map map = new HashMap();
    map.put("hello", "你好");
    System.out.pirntln((String)map.get("hello"));
}

顯然,在前端編譯過程中,對象的泛型類型被擦除,轉換為了沒有泛型的裸類型。而在位元組碼文件的相關位置插入了強制類型轉換程式碼,從而實現泛型。

類型擦除式的泛型帶來了幾個嚴重的問題:

Ⅰ. 不支援基礎數據類型的泛型

由於我們無法在 int、long 等基礎數據類型和 Object 之間強制轉型,所以 Java 的泛型不支援基礎數據類型。

Ⅱ. 運行時無法獲取泛型類型資訊

加入我們想寫一個泛型版本的 List 轉換為數組的方法,由於不能再運行時獲取泛型資訊,只能再傳入一個元素的類型。

public static <T> T[] convert(List<T> list, Class<T> componentType){
    Tp[] array = (T[])Array.newInstance(componentType, list.size());
}

Ⅲ. 無法正常的實現重載等功能

例如兩個方法我們試圖依賴泛型類型不同來實現重載,就會發生編譯錯誤。因為泛型類型在前端編譯器被擦除了,變成了兩個一模一樣的方法。

3. 橋接方法的機制

當一個類實現一個泛型介面時,泛型介面在編譯後類型被擦除,這樣我們在實現類中就不能找到對應介面的實現方法。為了解決這個問題,Java 在編譯相關類時使用了一個橋接方法的機制,通過為實現類新增一個橋接方法,來實現類型擦除後介面的方法。

image-20220222114826866

Tags: