泛型

wen-210162704027發表於2024-08-07

泛型

目錄
  • 泛型
    • 泛型的定義
    • 泛型的作用
    • 泛型的特性
    • 泛型的使用

泛型的定義

Java泛型是在Java SE 5中引入的一種特性,它允許你為你的類、介面和方法指定型別引數,從而使得程式碼更加型別安全和靈活。泛型的本質是引數化型別,即在編譯時提供型別資訊,以確保型別的正確性。

java複製public class Box<T> {
    private T t;

    public void set(T t) { this.t = t; }
    public T get() { return t; }
}

泛型的作用

  • 型別安全:泛型提供了編譯時型別檢查,減少了執行時型別錯誤。
  • 消除型別轉換:使用泛型可以減少型別轉換,使程式碼更簡潔。
  • 程式碼複用:泛型允許你編寫更通用的類和方法,可以用於不同的資料型別。

泛型的特性

Java泛型是Java 5 引入的一個特性,它為Java語言帶來了型別安全和程式碼複用性。以下是Java泛型的主要特性:

  • 型別安全
    泛型的主要目的是提供型別安全。使用泛型,你可以在編譯時檢查型別錯誤,避免在執行時出現型別轉換異常。

  • 消除型別轉換
    泛型消除了在集合中儲存和檢索物件時需要進行的型別轉換。這使得程式碼更簡潔,減少了出錯的可能性。

  • 程式碼複用
    泛型允許你編寫可以用於多種型別的通用類和方法,從而提高程式碼複用率。

  • 型別引數化
    泛型允許你為類、介面、方法定義型別引數,這些引數在例項化或呼叫時指定。

  • 型別推斷
    Java編譯器能夠推斷泛型的型別,這減少了程式碼的冗餘,使得程式碼更加簡潔。

  • 泛型萬用字元
    使用問號?可以定義一個萬用字元型別,它可以接受任何型別的引數。

  • 型別上限
    使用extends關鍵字,可以為泛型指定一個型別上限,即泛型引數必須是上限型別或其子型別。

  • 型別下限(不常用):
    使用super關鍵字,可以為泛型指定一個型別下限,但這種做法在Java中不常用。

  • 協變與逆變
    泛型是協變的,意味著如果ST的子型別,則List<S>List<T>的子型別。逆變通常用於方法引數,但Java泛型不支援泛型的逆變。

  • 型別擦除
    泛型的型別資訊在編譯時存在,但在執行時被擦除,這意味著執行時無法直接獲取泛型的型別資訊。

  • 泛型陣列限制
    由於型別擦除,Java不允許建立泛型型別的陣列。

  • 非重新例項化
    泛型類和介面不能被重新例項化,即不能使用泛型引數建立它們的子類。

  • 與現有程式碼的相容性
    泛型被設計為與現有非泛型程式碼相容,這允許逐步將泛型整合到現有程式碼庫中。

  • 限制與原始型別
    當泛型型別使用未指定實際型別時,它被稱為原始型別(如List而不是List<String>)。使用原始型別會失去泛型的型別安全。

  • 橋方法
    當泛型類繼承非泛型類時,編譯器會生成橋方法來保持二進位制相容性。

泛型的使用

定義泛型類

public class Box<T> {
    private T item;

    public void setItem(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }
}

使用泛型類

Box<String> stringBox = new Box<>();
stringBox.setItem("Hello, World!");
String item = stringBox.getItem();

泛型介面

public interface Stack<T> {
    void push(T item);
    T pop();
}

泛型方法

public class Utils {
    public static <T> void printArray(T[] array) {
        for (T item : array) {
            System.out.println(item);
        }
    }
}

使用泛型方法

String[] strings = {"a", "b", "c"};
Utils.printArray(strings);
Integer[] numbers = {1, 2, 3};
Utils.printArray(numbers);

泛型萬用字元

public class WildcardTest {
    public static void printArrayWithBounds(List<? extends Number> list) {
        for (Number n : list) {
            System.out.println(n);
        }
    }

    public static void printArrayWithUnboundedWildcard(List<?> list) {
        for (Object obj : list) {
            System.out.println(obj);
        }
    }
}

泛型的型別限定

public class GenericClass<T extends Comparable<T>> {
    // T 必須實現了 Comparable 介面
    public void compare(T a, T b) {
        a.compareTo(b);
    }
}

泛型方法的型別限定

public class MathUtils {
    public static <T extends Number> double calculate(T a, T b, MathOperation<T> operation) {
        return operation.performOperation(a, b);
    }
}

interface MathOperation<T extends Number> {
    double performOperation(T a, T b);
}

泛型的型別擦除

Java泛型的型別資訊在編譯時被檢查,但在執行時會被擦除,這意味著你不能直接獲取泛型的型別資訊:

Box<String> box = new Box<>();
System.out.println(box.getItem().getClass()); // 正確使用
System.out.println(box.getClass().getTypeParameters()); // 編譯錯誤,因為型別資訊已被擦除

泛型陣列的限制

由於型別擦除,Java不允許建立泛型陣列:

List<String>[] lists = new List<String>[10]; // 編譯錯誤
List<String>[] lists = new ArrayList<String>[10]; // 正確

使用原始型別

有時,為了與舊程式碼相容,你可能需要使用原始型別:

List list = new ArrayList(); // 使用原始型別
list.add("String"); // OK
list.add(1); // 編譯時不報錯,但執行時會丟擲 ClassCastException

使用泛型時,應該儘量避免使用原始型別,以保持型別安全。

透過這些用法,你可以建立型別安全、靈活且可重用的程式碼。Java泛型是Java語言中一個非常重要的特性,廣泛應用於集合框架、自定義資料結構和演算法實現中。

相關文章