1. 程式人生 > >Java筆記:泛型

Java筆記:泛型

顯式 end 接口 運行時 get java代碼 我們 open tcl

一、簡單實例

技術分享圖片
class Solution<T> {
    private T obj;

    Solution(T obj) {
        this.obj = obj;
    }

    public T getObj() {
        return obj;
    }

    public static void main(String[] args) {
        Solution<Integer> solution = new Solution<>(100);
        Object obj = solution.getObj();
        System.out.println(obj.getClass().getName());
//java.lang.Integer System.out.println(obj);//100 } }
View Code


二、類型安全

若簡單地使用Object來替代泛型並確保類型轉換正確,那麽即使不適用泛型也能得到相同的功能。但是如果類型轉換不正確,程序就會在運行時發生錯誤,使用泛型可以自動確保類型安全,這個過程中消除了手動輸入類型轉換以及類型檢查的需要,運行時的錯誤就可以轉換為編譯時的錯誤,這是泛型的主要優勢。

三、多個類型參數

技術分享圖片
class Solution<T, V> {
    private T objA;
    private V objB;

    Solution(T objA, V objB) {
        
this.objA = objA; this.objB = objB; } public T getObjA() { return objA; } public V getObjB() { return objB; } public static void main(String[] args) { Solution<Character, Integer> solution = new Solution<>(‘0‘, 0); System.out.println(solution.getObjA()); System.out.println(solution.getObjB()); } }
View Code

四、有界類型

通過指定超類限制能夠傳遞給類型參數的類型。

技術分享圖片
class Solution<T extends Number> {
    private T[] arr;

    double getSum() {
        double sum = 0;
        for (T i : arr)
            sum += i.doubleValue();
        return sum;
    }

    Solution(T... nums) {
        arr = nums;
    }

    public static void main(String[] args) {
        Solution<Integer> solution = new Solution<>(1, 2, 3, 4, 5);
        System.out.println(solution.getSum());
    }
}
View Code

除了指定超類作為邊界之外,還可以指定接口作為邊界。

五、通配符參數

技術分享圖片
boolean sameSum(Solution<T> ob) {
        return getSum() == ob.getSum();
}
View Code

假設我們需要比較具有不同泛型參數的相同類型對象,上述代碼並不能滿足要求。只有當比較對象具有相同的泛型參數時,上述代碼才可以被調用。正確的解決方法需要用到通配符,通配符同樣可指定為有界。

技術分享圖片
class Solution<T extends Number> {
    private T[] arr;

    double getSum() {
        double sum = 0;
        for (T i : arr)
            sum += i.doubleValue();
        return sum;
    }

    boolean sameSum(Solution<? extends Number> ob) {
        return getSum() == ob.getSum();
    }

    Solution(T... nums) {
        arr = nums;
    }

    public static void main(String[] args) {
        Solution<Integer> solutionA = new Solution<>(1, 2, 3, 4, 5);
        Solution<Double> solutionB = new Solution<>(5.0, 4.0, 3.0, 2.0, 1.0);
        System.out.println(solutionA.sameSum(solutionB));//true
    }
}
View Code

六、泛型方法

技術分享圖片
class Solution {
    static <T extends Number, V extends Number> boolean contain(T[] arr, V x) {
        for (int i = 0; i < arr.length; i++)
            if (arr[i].doubleValue() == x.doubleValue())
                return true;
        return false;
    }

    public static void main(String[] args) {
        Double[] arr = {1.0, 2.0, 3.0, 4.0, 5.0};
        System.out.println(contain(arr, 1));
    }
}
View Code

構造函數同樣支持泛型。

技術分享圖片
class Solution {
    private double number;

    <T extends Number> Solution(T t) {
        number = t.doubleValue();
    }
}
View Code

七、泛型接口

泛型接口與泛型類相似。

技術分享圖片
interface MinMax<T extends Comparable<T>> {
    T min();
    T max();
}

class Solution<T extends Comparable<T>> implements MinMax<T> {
    private T[] arr;

    @Override
    public T min() {
        T res = arr[0];
        for (int i = 1; i < arr.length; i++)
            if (res.compareTo(arr[i]) > 0)
                res = arr[i];
        return res;
    }

    @Override
    public T max() {
        T res = arr[0];
        for (int i = 1; i < arr.length; i++)
            if (res.compareTo(arr[i]) < 0)
                res = arr[i];
        return res;
    }

    Solution(T... nums) {
        arr = nums;
    }

    public static void main(String[] args) {
        Solution<Integer> ob = new Solution<>(1, 2, 3, 4, 5);
        System.out.println(ob.min());
        System.out.println(ob.max());
    }
}
View Code

八、歷史遺留

早期的Java是不支持泛型的,現在仍然存在大量歷史遺留代碼。為了使這些遺留代碼保留功能同時又和泛型兼容,Java允許使用泛型類而不提供任何類型參數,這會為類創建原始類型。原始類型與不使用泛型的遺留代碼是兼容的,但失去了泛型的類型安全性。本質上就是使用Object替換了類型參數所表示的類型。

技術分享圖片
class Solution<T> {
    private T ob;

    Solution(T ob) {
        this.ob = ob;
    }

    T getOb() {
        return ob;
    }

    public static void main(String[] args) {
        Solution solution = new Solution(new String("Hello World"));
        System.out.println(solution.getOb());
    }
}
View Code

九、泛型類層次

使用泛型超類。

技術分享圖片
class A<T> {
    T a;

    A(T a) {
        this.a = a;
    }

    T getA() {
        return a;
    }
}

class B<T, V> extends A<T> {
    V b;

    B(T a, V b) {
        super(a);
        this.b = b;
    }

    V getB() {
        return b;
    }
}
View Code

泛型層次比較。

技術分享圖片
class Solution {
    public static void main(String[] args) {
        B<String, String> obB = new B<>("Hello", "World");
        System.out.println(obB instanceof A<?>);//true
    }
}
View Code

當需要對泛型類進行強制轉換時,必須確保其相互兼容並且泛型參數相同。

十、擦除

為了兼容以前的Java版本,對Java語言的語法或虛擬機所做的任何修改必須避免破壞以前的代碼,所以Java使用擦除實現泛型。

擦除的工作原理:編譯Java代碼時,所有泛型信息被擦除。那麽必須使用他們的界定類型替換類型參數,如果沒有顯式指定界定類型,就會使用Object。之後進行適當的類型轉換以保持與類型參數所指定類型的兼容。這意味著允許時時沒有類型參數的,Java泛型只是一種源代碼機制,運行時的類型轉換必然會帶來開銷。

十一、模糊性錯誤

技術分享圖片
class Solution<T, V> {
    private T a;
    private V b;

    Solution(T a, V b) {
        this.a = a;
        this.b = b;
    }

    void set(T a) {
        this.a = a;
    }

    void set(V b) {
        this.b = b;
    }
}
View Code

上述代碼出現了模糊性錯誤,當T和V為相同類型時,無法確定調用的方法。

十二、限制

  • 不能實例化類型參數的對象或數組。
  • 靜態成員不能使用在類中聲明的類型參數。
  • 泛型類不能擴展Throwable,即不能創建泛型異常類。

若要實例化泛型對象的數組,需要使用通配符。

技術分享圖片
class Solution<T> {
    public static void main(String[] args) {
        Solution<Integer>[] solutionsA = new Solution<Integer>[10];//錯誤
        Solution<?>[] solutionsB = new Solution<?>[10];//正確
    }
}
View Code

Java筆記:泛型