理解 Java 泛型最簡單的方式,是將其視為一種方便的語法糖,能省去手動進行類型轉換的麻煩:
List<Apple> box = ...;
Apple apple = box.get(0); // 無需強制轉型
若無泛型,程式碼必須寫成:
List box = ...;
Apple apple = (Apple) box.get(0); // 需要手動轉型
泛型的核心優在於:讓編譯器保存參數類型資訊、執行類型檢查、自動處理類型轉換。編譯器保證這些轉換的正確性,避免了執行時期才發現的 ClassCastException,大幅提升程式碼安全性與可維護性。
泛型引入了類型變數的概念,依據 Java 語言規範,類型變數出現在以下四種宣告中:
當一個類別或介面擁有一個或多個類型變數時,它就是泛型。類型變數由尖括號界定,置於類別或介面名稱之前:
public interface List<T> extends Collection<T> { ... }
類型變數的角色如同參數,提供給編譯器進行類型檢查的資訊。Java 類別庫中的眾多類別(如整個 Collection 框架)都已泛型化。例如 List<Apple> 中的 get 方法實際上返回 T 類型物件,編譯器會自動進行類型轉換。
方法與建構子若宣告了一或多個類型變數,同樣可泛型化:
public static <T> T getFirst(List<T> list)
// 寫入:編譯器阻擋錯誤類型
List<String> str = new ArrayList<>();
str.add("Hello ");
str.add("World.");
// str.add(1); // 編譯錯誤
// 讀取:自動獲得正確類型
String myString = str.get(0);
// 遍歷:Iterator 亦泛型化
for (Iterator<String> iter = str.iterator(); iter.hasNext();) {
String s = iter.next();
System.out.print(s);
}
// 增強 for 迴圈
for (String s : str) {
System.out.print(s);
}
使用泛型時,autoboxing/autounboxing 會自動發生:
List<Integer> ints = new ArrayList<>();
ints.add(0); // 自動裝箱 int -> Integer
ints.add(1);
int sum = 0;
for (int i : ints) { // 自動拆箱 Integer -> int
sum += i;
}
注意:裝箱/拆箱會帶來效能損耗,需謹慎使用。
Map<K, V>)。extends 限定上界(有界類型)。?、? extends T、? super T)。範例一:使用泛型
public class Gen<T> {
private T ob;
public Gen(T ob) { this.ob = ob; }
public T getOb() { return ob; }
public void setOb(T ob) { this.ob = ob; }
public void showType() {
System.out.println("T 的實際類型是: " + ob.getClass().getName());
}
}
public class GenDemo {
public static void main(String[] args) {
Gen<Integer> intOb = new Gen<>(88);
intOb.showType();
int i = intOb.getOb(); // 無需轉型
System.out.println("value= " + i);
Gen<String> strOb = new Gen<>("Hello Gen!");
strOb.showType();
String s = strOb.getOb();
System.out.println("value= " + s);
}
}
範例二:不使用泛型
public class Gen2 {
private Object ob;
public Gen2(Object ob) { this.ob = ob; }
public Object getOb() { return ob; }
public void setOb(Object ob) { this.ob = ob; }
public void showType() {
System.out.println("實際類型是: " + ob.getClass().getName());
}
}
public class GenDemo2 {
public static void main(String[] args) {
Gen2 intOb = new Gen2(88); // 自動裝箱
intOb.showType();
int i = (Integer) intOb.getOb(); // 必須強制轉型
System.out.println("value= " + i);
Gen2 strOb = new Gen2("Hello Gen!");
strOb.showType();
String s = (String) strOb.getOb(); // 必須強制轉型
System.out.println("value= " + s);
}
}
兩者執行結果相同,但泛型版本在編譯期保證類型安全,程式碼更簡潔、可讀性更高。掌握這點,基本泛型應用與程式碼閱讀即無障礙。