泛型程序設計

泛型介紹

泛型程序設計(Generic programming):可以被很多不同的類型的對象所重用。比那些直接使用Object變量,然後強制類型的轉換的代碼具有更好的安全性和可讀性。

使用類型參數(type parameters)可以將需要使用的類型,提前聲明。如:

ArrayList<String> list = new ArrayList<String>();

使用類型參數可以告知這個類適用於什麼類型,當調用對應的get()方法的時候,不需要進行強制類型轉換,編譯器本身就知道其對應的類型。

當實現一個泛型的時候非常不容易,因爲你需要知道這個這個類對應的所有用途及其類型,所以java提供了通配符類型,來解決這個問題。

定義簡單泛型類

泛型類,就是指具有一個或者多個類型變量,也就是說這個類適應這幾種類型。

使用類型變量T,用<>括起來,放在類名後面。這個泛型可以有多個類型變量,如<T,U>

可以使用類定義的類型變量指定類中屬性和方法的類型。

public class Pari<T> {

    private T first;
    private T second;
    
    public Pari(){
        first = null;
        second = null;
    }
    public Pari(T first,T second){
        this.first = first;
        this.second = second;
    }
    
    public T getFirst(){
        return first;
    }
    
    public T getSecond(){
        return second;
    }
    
    public void setFirst(T value){
        first = value;
    }
    public void setSecond(T value){
        second = value;
    }
}


其實泛型類可以看做是普通類的工廠。

泛型方法

泛型方法既可以在普通類中,也可以在泛型類中,定義方式是在方法名前加<T> T,說明該方法是泛型方法

public static <T> T getText(T param){
		return param;
	}

類型變量的限定

有的時候,比如對於特定的方法執行特定的操作,但是該操作不適用於一些類型,這時可以對類型變量T設置限定,可以使其集成特別的類或者接口(沒錯,在這裏對於接口也是使用繼承,因爲使用extends更加接近子類的意思),一個類型變量或通配符可以有多個限定

比如:T extends Comparable & Srializable

	public static <T extends Comparable> Pari<T> getMinMax(T[] word){
		
		if(word == null || word.length == 0)
			return null;
		T min = word[0];
		T max = word[0];
		for(int i=1;i<word.length;i++){
			if(word[i].compareTo(max) > 0)
				max = word[i];
			if(word[i].compareTo(min) < 0)
				min = word[i];
		}
		return new Pari<T>(min,max);
	}

注意:

JVM中沒有泛型,只有普通的類和方法

所有的類型參數都是用他們的限定類型轉換(如果沒有類型參數,則使用Object類型),這個過程稱爲擦除(erased),擦除類型變量,並替換爲限定類型

有時爲保持類型安全性,需要插入強制類型轉換


約束和侷限性

不能用基本類型實例化類型參數

不能用類型參數代替基本類型:因此Pari<double>是錯誤的,需要使用pari<Double>,原因是類型擦除,擦除之後爲Object類型,object類型並不能存儲double值

類型查詢只適用於基本類型

虛擬機中的對象總有一個特定的非泛型類型。因此,所有類型查詢只產生原始類型。

如:if(a instanceof Pari<T>)是錯誤的,因爲只能查詢原始類型,即Pari

又如:

Pari<String> pari1 = ...

Pari<Employee> pari2 =...

if (pari1.getClass() == pari2.getClass())返回true,因爲兩次getClass()都是返回Pari.class

不能創建參數化類型數組

不能實例化參數類型數組

Pari<Sting>[] table = new Pari<String>[10];

因爲當類型擦除後,爲Pari[]類型,即可以轉化爲Object[]類型,這時候如果 table[0] = new Pari<Employee>();這時候就會拋出ArrayStoreException異常,所以,不允許創建參數類型數組。但是可以聲明。如果想要實現可以使用通配類型數組然後進行強制類型轉換

Pari<String>[] table = (Pari<String>[]) new Pari<?>[10];

不能實例化類型變量

new T()或者T.class這些都是非法的,因爲類型擦除後爲new Object(),這並不是想要的。

public Pari(){
     first = new T();
     second = new T();
}

但是可以通過反射調用Class.newInstance方法構造

public static<T> Pari<T> makePari(Class<T> c){
     return new Pari<>(c.newInstance(),c.newInstance())
}

泛型類的靜態上下文中類型無效

public static class Singleton<T>{
     public static T s1;
     public static getS1(){
         return  s1 
     }
}
注意只是在靜態類中不可以使用,普通類中可以使用。

不能拋出或捕獲泛型類的實例

public class Problem<T> extends Exception{}是不對的。但是可以在異常規範中使用類型變量,意思就是可以在拋出異常的方法中使用類型變量 


泛型類繼承規則

泛型類允許繼承或者擴展其他的泛型類,如ArrayList<T>類實現List<T>接口,即一個ArrayList<Manager>可以被轉換爲List<Manger>。

但是ArrayList<Manager>不能夠實現ArrayList<Employee>或List<Employee>。

所以記住,泛型的繼承實現,需要保證其類型參數的一致。


通配符類型

固定的泛型類型系統,存在很多的侷限性,爲了解決這一點,引入了:通配符類型。

帶有子類型的限定

Pari<? extends Employee>
表示任何泛型Pari類型,他的類型參數是Employee的子類,如Pari<Manager>,當使用了通配符泛型,就可以將Pari<Manager>,給以這個Pari<? extends Employee>參數的函數了。

通配符的超類型限定

通配符限定與類型變量限定十分類似,但是可以指定一個超類型限定。

? super Manager

這個通配符限定了Manager的所有的超類,這與上面講的通配符繼承恰恰相反,這時候Pari<Employee> 或者Pari<Object>可以實現或者繼承Pari<? super Manager>

帶有超類型限定的通配符可以向泛型對象寫入,帶有子類型限定的通配符可以從泛型對象讀取。

需要注意,通配符不是類型變量,不能像之前的泛型類型參數一樣定義變量或者方法

? t = p.getFirst()這是錯誤的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章