泛型
- 泛型
- 泛型的定義
- 泛型的作用
- 泛型的特性
- 泛型的使用
泛型的定義
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中不常用。 -
協變與逆變:
泛型是協變的,意味著如果S
是T
的子型別,則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語言中一個非常重要的特性,廣泛應用於集合框架、自定義資料結構和演算法實現中。