Java泛型入門:爲什麼用泛型?簡單理解與使用

爲什麼要用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是佔位符,可自定義名稱,如EKV等)。

示例:定義一個通用的“盒子”類

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標準庫中的集合類(如ArrayListHashMap)都支持泛型,這是泛型的典型應用場景。

示例:使用泛型集合

// 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

通配符:讓泛型更靈活

通配符<?>用於處理泛型集合的類型範圍,常見的有兩種:

  1. 上界通配符<? extends T>
    表示集合中的元素只能是T或其子類(T爲上界)。
    示例:允許存儲Number及其子類(IntegerDouble等)
   List<? extends Number> numbers = new ArrayList<Integer>();
   numbers.add(new Integer(10)); // 錯誤!不允許添加元素
   Number num = numbers.get(0); // 正確,返回Number類型
  1. 下界通配符<? super T>
    表示集合中的元素只能是T或其父類(T爲下界)。
    示例:允許存儲Integer及其父類(NumberObject等)
   List<? super Integer> ints = new ArrayList<Object>();
   ints.add(new Integer(10)); // 正確
   Object obj = ints.get(0); // 正確,返回Object類型

泛型的核心優勢

  1. 類型安全:編譯時檢查類型,避免運行時ClassCastException
  2. 消除強制轉換:無需手動強轉,代碼更簡潔。
  3. 代碼複用:通過類型參數實現一套代碼處理多種類型,無需重複編寫邏輯。

注意事項

  1. 不能使用基本類型:泛型類型必須是引用類型(如int需用Integerdouble需用Double)。
   List<int> nums = new ArrayList<>(); // 錯誤!需用List<Integer>
  1. 泛型不支持繼承List<String>List<Object>是平級的,不能互相賦值。
   List<String> strList = new ArrayList<>();
   List<Object> objList = strList; // 錯誤!泛型類型不可繼承
  1. 類型擦除:運行時泛型類型會被擦除(T被替換爲Object),因此泛型類中無法直接使用T的實例化(如new T())。

總結

泛型是Java中解決類型安全和代碼複用的重要特性,通過參數化類型讓集合和類更靈活、更安全。掌握泛型的核心在於理解類型參數泛型類/方法/接口以及通配符的使用場景,多通過簡單示例練習,就能快速上手。

通過本文的示例和講解,希望你能初步理解Java泛型的價值和用法。從簡單的集合泛型開始,逐步嘗試泛型類和方法,很快就能熟練運用泛型提升代碼質量啦!

小夜