天天看點

Java 泛型機制詳解

作者:程式猿凱撒
Java 泛型機制詳解

一、Java 中為什麼會引入泛型?

Java 引入泛型的主要目的是為了提高代碼的類型安全性和可讀性。在 Java 5 之前,集合架構中的容器可以存儲任意類型的對象,這就使得程式員需要在運作時進行類型轉換,容易引發類型轉換異常。而引入泛型後,集合架構中的容器可以限定存儲的元素類型,使得程式員可以在編譯時進行類型檢查,避免了類型轉換異常的發生。此外,泛型還可以提高代碼的可讀性和可維護性,使得代碼更易于了解和修改。泛型的引入使得 Java 語言更加類型安全,更加适合大規模軟體開發。

接下來我們舉個例子說明為什麼: 假設有一個需求,需要編寫一個方法,用于比較兩個對象是否相等。最初的實作可能是這樣的。

java複制代碼public static boolean compareObject(String s1, String s2) {
    return s1.equals(s2);
}
           

但是入參不一定是 String,也有可能是 int、long 等,那麼該怎麼寫呢?難道每個都寫一遍?

java複制代碼public static boolean compareObject(String s1, String s2) {
    return s1.equals(s2);
}

public static boolean compareObject(Integer s1, Integer s2) {
    return s1.equals(s2);
}
           

這樣的代碼可讀性,可維護性都很差,于是我們用泛型進行改進, 一個方法就搞定了。

java複制代碼public static <T> boolean compareObjects(T o1, T o2) {
    return o1.equals(o2);
}
           

二、泛型的應用

泛型是 Java 中的一個重要特性,它可以讓我們編寫更加通用、可複用的代碼。

java複制代碼package com.pany.camp.base;

public class Genericity<T, U> {

    private T first;
    private U second;

    public Genericity(T first, U second) {
        this.first = first;
        this.second = second;
    }

    public T getFirst() {
        return first;
    }

    public U getSecond() {
        return second;
    }

    public void setFirst(T first) {
        this.first = first;
    }

    public void setSecond(U second) {
        this.second = second;
    }

    @Override
    public String toString() {
        return "(" + first + ", " + second + ")";
    }
}

           

這個示例中定義了一個泛型類 Genericity,它有兩個類型參數 T 和 U,分别表示 Genericity 中的兩個元素的類型。Genericity類有一個構造函數,用于初始化 Genericity對象的兩個元素。Genericity 類還有一些方法,用于擷取和設定 Genericity 對象的元素,以及将 Genericity 對象轉換成字元串表示。

java複制代碼package com.pany.camp.base;

public class GenericityMain {

    public static void main(String[] args) {
        Genericity<String, Integer> pair = new Genericity<>("Hello", 123);

        // 輸出:(Hello, 123)
        System.out.println(pair);
    }
}

           
Java 泛型機制詳解

它的第一個元素是一個字元串,第二個元素是一個整數。使用泛型可以讓我們編寫更加通用、可複用的代碼,可以在不同的場景中使用相同的類來處理不同類型的資料。

三、泛型接口

java複制代碼package com.pany.camp.base;

public interface Pair<T, U> {

    T getFirst();

    U getSecond();
}

           

我們定義了一個泛型接口 Pair,它有兩個類型參數 T 和 U,分别表示 Pair 中的兩個元素的類型。Pair 接口有兩個方法,getFirst() 和 getSecond(),用于擷取 Pair 對象的兩個元素。

java複制代碼package com.pany.camp.base;

public class StringIntegerPair implements Pair<String, Integer> {

    private String first;
    private Integer second;

    public StringIntegerPair(String first, Integer second) {
        this.first = first;
        this.second = second;
    }

    @Override
    public String getFirst() {
        return first;
    }

    @Override
    public Integer getSecond() {
        return second;
    }
}
           

然後我們建立了一個 StringIntegerPair 類,它實作了 Pair 接口,并指定了 T 和 U 的具體類型為 String 和 Integer。StringIntegerPair 類有一個構造函數,用于初始化 StringIntegerPair 對象的兩個元素。StringIntegerPair 類還實作了 Pair 接口的兩個方法,用于擷取 StringIntegerPair 對象的兩個元素。

四、泛型方法

泛型方法可以讓我們編寫更加通用、可複用的代碼,可以在不同的場景中使用相同的方法來處理不同類型的資料。

例子如下:

bash複制代碼public static <T> int countOccurrences(T[] array, T element) {
    int count = 0;
    for (T value : array) {
        if (value.equals(element)) {
            count++;
        }
    }
    return count;
}
           

我們定義了一個泛型方法 countOccurrences,它有兩個參數,一個是泛型類型數組 array,另一個是泛型類型元素 element。countOccurrences 方法的作用是統計數組中與 element 相等的元素個數。

bash複制代碼String[] strings = {"foo", "bar", "baz", "foo"};
int count1 = countOccurrences(strings, "foo"); // 傳回 2
int count2 = countOccurrences(strings, "qux"); // 傳回 0

Integer[] integers = {1, 2, 3, 2, 1};
int count3 = countOccurrences(integers, 2); // 傳回 2
int count4 = countOccurrences(integers, 4); // 傳回 0
           

我們分别使用了字元串類型數組和整數類型數組來調用 countOccurrences 方法,傳入不同的元素來統計數組中相等的元素個數。

五、泛型的上下限

泛型的上下限是指在使用泛型時,可以限制泛型參數的類型範圍,進而提高代碼的類型安全性。上限限制了泛型參數的類型必須是某個類或其子類,下限限制了泛型參數的類型必須是某個類或其父類。 使用泛型的上下限可以避免類型轉換錯誤,提高代碼的類型安全性。

在 Java 中,可以使用 extends 關鍵字來指定泛型的上限,使用 super 關鍵字來指定泛型的下限。例如:

bash複制代碼public class Box<T> {
    private T item;
    public void setItem(T item) {
        this.item = item;
    }
    public T getItem() {
        return item;
    }
}

// 使用 extends 指定泛型的上限
public class NumberBox<T extends Number> {
    private T item;
    public void setItem(T item) {
        this.item = item;
    }
    public T getItem() {
        return item;
    }
}

// 使用 super 指定泛型的下限
public class IntegerBox<T super Integer> {
    private T item;
    public void setItem(T item) {
        this.item = item;
    }
    public T getItem() {
        return item;
    }
}
           

NumberBox 類使用 extends 指定泛型參數的上限為 Number 類型及其子類,是以隻能使用 Number 類型及其子類作為泛型參數。而 IntegerBox 類使用 super 指定泛型參數的下限為 Integer 類型及其父類,是以隻能使用 Integer 類型及其父類作為泛型參數。

作者:激流丶

連結:https://juejin.cn/post/7241765112921735227