泛型介紹
泛型程序設計(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()這是錯誤的。