Java之泛型剖析

 泛型程序設計意味着編寫的代碼可以被很多不同類型的對象所重用。泛型提供了一個類型參數用來指示元素的類型。比如AarryList:
 ArrayList<Sting> strs = new ArrayList<String>();(根據多態性,前面的ArrayList<String>可以換成List<String>)。這樣使代碼有更好的可讀性。

一個泛型類就是具有一個或多個類型變量的類。下面是一個簡單的泛型類:

 public class Result<T> {
    private T first;
    private T second;

    public T getFirst() {
        return first;
    }

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

    public T getSecond() {
        return second;
    }

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

    public static <T> T getObj(Class<T> cls) throws IllegalAccessException, InstantiationException {
        return cls.newInstance();
    }
}
Result類中引入類型變量T,用尖括號(<>)括起來,並放在類後面。泛型類可以有多個類型變量,其中第一個域和第二個域可以有不同的類型:public class Result<T, U>{....}。類定義中的類型變量指定方法的返回類型以及域和局部變量的名字。
上面的例子中還定義了一個泛型方法getObj()。當然泛型方法也可以在普通類中定義。

類型變量的限定

有時,類或方法需要對類型加以約束
public class ArrayTest {
    public static <T extends Comparable> T min(T[] a) {
        if (a == null || a.length == 0) {
            return null;
        }
        T temp = a[0];
        for (int i = 1; i < a.length; ++i) {
            if (temp.compareTo(a[i]) > 0) {
                temp = a[i];
            }
        }
        return temp;
    }
}
這裏對T的類型限制爲實現了Comparabe接口的類。之所以這樣做是因爲,只有實現了Comparabe接口的類纔會實現compareTo()方法。程序才能正確編譯執行。
(這裏需要注意的是Comparabe接口本身就是一個泛型類型)。
<T extends BoundingType>表示T應該是綁定類型(BoundingType)的子類型。T和綁定的類型可以是類也可以是接口。(均使用extends關鍵字)。
一個類型變量或通配符可以有多個限定,例如:
T extends Comparable & Serializable
限定類型用“&”分隔,而逗號用來分隔類型變量。注意點:Java的繼承中,可以根據需要擁有多個接口超類型,但限定至多有一個類。如果一個類作爲限定,它必須是限定列表中的第一個。

泛型的擦除

虛擬機沒有泛型類型對象,所有類型都屬於普通類。當定義一個泛型類型時,都自動提供了一個相應的原始類型。原始類型就是刪去類型參數後的泛型名。擦除類型變量,並替換爲限定類型。
原始類型用第一個限定的類型變量來替換,如果沒有給定限定就用Object替換。比如上面的例子,因爲T無類型限制,所以會被用Object代替。例子如下:
public class ResultSecond<T extends Comparable & Serializable> implements Serializable {
    private T first;
    private T second;

    public ResultSecond(T first, T second) {
        if (first.compareTo(second) > 0) {
            this.first = first;
            this.second = second;
        } else {
            this.first = second;
            this.second = first;
        }
    }
}

原始類型如下:

public class ResultSecond {
    private Comparable first;
    private Comparable second;

    public ResultSecond(Comparable first, Comparable second) {
        if (first.compareTo(second) > 0) {
            this.first = first;
            this.second = second;
        } else {
            this.first = second;
            this.second = first;
        }
    }
}

幾個注意點

1、不能用基本類型實例化類型參數。
因此沒有Result<int>,只有Result<Integer>。原因是類型擦除。擦除後,Result類含有Object類型的域,而Object不能存儲int值。
2、運行時類型查詢只適用於原始類型。
虛擬機中的對象總有一個特定的非泛型類型。因此所有的類型檢查只產生原始類型。例如:if (a instanceof Result<T>),僅僅只是測試a是否是任意類型的一個Result。
3、不能創建參數化類型的數組。
不能實例化參數化類型的數組。
例如:Result<String>[] result = new Result<String>[5];這是不被允許的,因爲擦除之後,result類型就是Result[]類型。
這裏只是不允許創建數組,但是聲明爲Result<String>[]的變量仍是合法的。不過不能用new Result<String>[10]初始化這個變量。
4、不能實例化類型變量。不能使用new T(...),new T[...]。
5、泛型類的靜態上下文類型變量無效。即不能在靜態域或方法中引用類型變量。
6、不能拋出或捕獲泛型類的實例
7、注意擦除後的衝突。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章