爲什麼要用Java泛型?¶
在Java中,我們經常需要處理各種數據集合,比如存儲學生信息、商品信息等。如果不使用泛型,直接用Object類型存儲數據,雖然靈活,但會帶來兩個主要問題:類型不安全和強制類型轉換繁瑣。
舉個例子,沒有泛型時,我們可能這樣寫:
// 沒有泛型的集合,存儲Object類型
List list = new ArrayList();
list.add("張三");
list.add(20); // 錯誤地添加了整數類型
// 取數據時需要強制轉換,容易出錯
String name = (String) list.get(0); // 正常
Integer age = (Integer) list.get(1); // 正常
String wrong = (String) list.get(1); // 運行時ClassCastException!
這裏的問題在於,list可以存儲任意類型的數據,編譯器無法提前檢查類型是否匹配,運行時一旦類型不匹配就會拋出異常。
泛型是什麼?¶
泛型(Generics)是Java 5引入的特性,允許我們將類型作爲參數傳遞給類、接口或方法,從而實現類型安全和代碼複用。簡單來說,泛型就是“參數化類型”,例如List<String>表示“只能存儲String類型的列表”。
如何使用泛型?¶
1. 泛型類¶
泛型類是最基礎的泛型應用,定義時在類名後用<T>聲明類型參數(T是佔位符,可自定義名稱,如E、K、V等)。
示例:定義一個通用的“盒子”類
class Box<T> {
private T content; // 用T作爲類型參數
public T getContent() {
return content;
}
public void setContent(T content) {
this.content = content;
}
}
使用泛型類:
// 存儲字符串的盒子
Box<String> stringBox = new Box<>();
stringBox.setContent("Hello");
String str = stringBox.getContent(); // 無需強制轉換,直接返回String
// 存儲整數的盒子
Box<Integer> intBox = new Box<>();
intBox.setContent(100);
Integer num = intBox.getContent(); // 直接返回Integer
通過泛型,Box可以靈活存儲任意類型的數據,且編譯器會在編譯時檢查類型是否匹配,避免運行時錯誤。
2. 泛型接口¶
泛型接口與泛型類類似,在接口名後聲明類型參數,實現類需指定具體類型。
示例:定義一個“生成器”接口
interface Generator<T> {
T generate(); // 接口方法返回T類型
}
// 實現泛型接口(指定具體類型)
class StringGenerator implements Generator<String> {
@Override
public String generate() {
return "Generic String";
}
}
// 使用
Generator<String> generator = new StringGenerator();
String result = generator.generate(); // 返回"Generic String"
3. 泛型方法¶
泛型方法是指方法本身帶有類型參數的方法,可在普通類或泛型類中定義。
示例:定義一個通用的“打印”方法
class Util {
// 泛型方法:接受任意類型的數組,返回第一個元素
public static <T> T getFirstElement(T[] array) {
if (array.length == 0) return null;
return array[0];
}
}
// 使用
String[] strArray = {"A", "B", "C"};
String firstStr = Util.getFirstElement(strArray); // 返回"A"
Integer[] intArray = {1, 2, 3};
Integer firstInt = Util.getFirstElement(intArray); // 返回1
4. 泛型集合(最常用)¶
Java標準庫中的集合類(如ArrayList、HashMap)都支持泛型,這是泛型的典型應用場景。
示例:使用泛型集合
// ArrayList存儲String類型
List<String> names = new ArrayList<>();
names.add("Alice"); // 正確
// names.add(123); // 編譯錯誤:類型不匹配,只能添加String
names.forEach(System.out::println); // 遍歷輸出:Alice
// HashMap存儲鍵值對(鍵:String,值:Integer)
Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 90); // 正確
scores.put("Bob", 85);
// scores.put("Charlie", "95"); // 編譯錯誤:值必須是Integer
Integer aliceScore = scores.get("Alice"); // 無需強制轉換,直接返回Integer
通配符:讓泛型更靈活¶
通配符<?>用於處理泛型集合的類型範圍,常見的有兩種:
- 上界通配符:
<? extends T>
表示集合中的元素只能是T或其子類(T爲上界)。
示例:允許存儲Number及其子類(Integer、Double等)
List<? extends Number> numbers = new ArrayList<Integer>();
numbers.add(new Integer(10)); // 錯誤!不允許添加元素
Number num = numbers.get(0); // 正確,返回Number類型
- 下界通配符:
<? super T>
表示集合中的元素只能是T或其父類(T爲下界)。
示例:允許存儲Integer及其父類(Number、Object等)
List<? super Integer> ints = new ArrayList<Object>();
ints.add(new Integer(10)); // 正確
Object obj = ints.get(0); // 正確,返回Object類型
泛型的核心優勢¶
- 類型安全:編譯時檢查類型,避免運行時
ClassCastException。 - 消除強制轉換:無需手動強轉,代碼更簡潔。
- 代碼複用:通過類型參數實現一套代碼處理多種類型,無需重複編寫邏輯。
注意事項¶
- 不能使用基本類型:泛型類型必須是引用類型(如
int需用Integer,double需用Double)。
List<int> nums = new ArrayList<>(); // 錯誤!需用List<Integer>
- 泛型不支持繼承:
List<String>和List<Object>是平級的,不能互相賦值。
List<String> strList = new ArrayList<>();
List<Object> objList = strList; // 錯誤!泛型類型不可繼承
- 類型擦除:運行時泛型類型會被擦除(
T被替換爲Object),因此泛型類中無法直接使用T的實例化(如new T())。
總結¶
泛型是Java中解決類型安全和代碼複用的重要特性,通過參數化類型讓集合和類更靈活、更安全。掌握泛型的核心在於理解類型參數、泛型類/方法/接口以及通配符的使用場景,多通過簡單示例練習,就能快速上手。
通過本文的示例和講解,希望你能初步理解Java泛型的價值和用法。從簡單的集合泛型開始,逐步嘗試泛型類和方法,很快就能熟練運用泛型提升代碼質量啦!