Java 泛型

最後更新: 2025-02-02 05:23:41 +0800 +0800
tags: Generics | Java
原文以英文撰寫。您正在閱讀AI翻譯版本。

Java 泛型提供編譯期型別檢查,同時保留與泛型出現前位元組碼的相容性。編譯器會抹除型別引數,在需要的位置插入轉型,並可能產生橋接方法,以在抹除後保留覆寫行為。因此 List<String> 與 List<Integer> 在執行時具有相同的類別,而泛型型別引數無法可靠地從物件中復原。

不變性與界限 #

泛型類別是不變的:List<Integer> 不是 List<Number>,因為後者會允許插入 Double。萬用字元在 API 邊界表達變異性。

java
static double sum(List<? extends Number> values) {
    return values.stream().mapToDouble(Number::doubleValue).sum();
}

static void addDefaults(List<? super Integer> destination) {
    destination.add(0);
}

從呼叫端讀取的生產者使用 extends,呼叫端寫入的消費者使用 super。List<? extends Number> 能產生 Number 值,但不接受任何非 null 值;List<? super Integer> 接受 Integer 值,但只能產生 Object。「唯讀」與「唯寫」只是簡寫,不是能力規則:兩種清單仍可查詢、清除,或在實作允許時接收 null。

捕獲與推斷 #

每個萬用字元都表示一個未知的捕獲型別。萬用字元容器無法表示兩個各自使用萬用字元的值具有相同型別,因此關係重要時請使用泛型輔助方法:

java
static void reverse(List<?> list) { reverseCaptured(list); }
private static <T> void reverseCaptured(List<T> list) {
    Collections.reverse(list);
}

方法推斷會結合引數限制、目標型別與宣告的界限。菱形運算子會為建構函式呼叫推斷型別引數;它不會讓原始型別變得安全。界限可以要求能力:<T extends Comparable<? super T>> T max(List<? extends T> values)。交集界限可以包含一個類別,後接多個介面。

抹除下的建構 #

new T()、new T[n] 與 T.class 都不合法,因為抹除不會保留執行時所需的建構函式、元件型別或類別常值。請改傳入工廠:

java
static <T> T create(Supplier<? extends T> factory) {
    return factory.get();
}

若要整合反射,請接受 Class<T> 並處理其已檢查反射例外;但呼叫端負責建構時,優先使用 Supplier<T>。泛型陣列仍受限制,因為陣列具體化且具協變性,而泛型型別會被抹除且具不變性。

參考資料 #

評論

在 GitHub 上閱讀或評論(發表評論需要登入)