Java基礎系列(三十四):泛型入門
為什麼需要泛型
使用泛型機制編寫的程式碼要比那些雜亂的使用Object
變數,然後再進行強制型別轉換的程式碼具有更好的安全性和可讀性,也就是說使用泛型機制編寫的程式碼可以被很多不同型別的物件所重用。
我們先來看看泛型程式設計的機制是如果演變的,這樣可以讓我們對泛型的作用有一個更深的認識。
泛型的演變
實際上在Java增加泛型機制之前就已經有一個ArrayList
類,這個ArrayList
類的泛型概念是使用繼承來實現的。
public class ArrayList {
private Object[] elementData;
public Object get (int i) {....}
public void add(Object o) {....}
}
這個類存在兩個問題:
當獲取一個值的時候必須進行強制型別轉換
ArrayList files = new ArrayList();
String filename = (String)files.get(0);
沒有錯誤檢查,可以向陣列中新增任何類的物件
files.add(new File(""));
對於這個呼叫,編譯和執行都不會出錯,但是當我們在其他地方使用get方法獲取剛剛存入的這個File
物件強轉為String
型別的時候就會產生一個錯誤。
泛型對於這種問題的解決方案是提供一個型別引數。
ArrayList<String> files = new ArrayList<String>();
//在JavaSE 7及以後的版本中,建構函式可以省略泛型型別:
ArrayList<String> files = new ArrayList<>();
這樣可以使程式碼具有更好的可讀性,我們一看就知道這個資料列表中包含的是String
物件。
編譯器也可以很好地利用這個資訊,當我們呼叫get
的時候,不需要再使用強制型別轉換,編譯器就知道返回值型別為String
,而不是Object
:
String filename = files.get(0);
編譯器還知道ArrayList<String>
中add
方法中有一個型別為String
的引數。這將比使用Object
型別的引數安全一些,現在編譯器可以檢查,避免插入錯誤型別的物件:
files.add(new File(""));
這樣的程式碼是無法通過編譯的,出現編譯錯誤比類在執行時出現類的強制型別轉換異常要好得多。
泛型類
一個泛型類就是具有一個或多個型別變數的類,對於這個類來說,我們只關注泛型,而不會為資料儲存的細節煩惱。
public class Pair<T> {
private T first;
private T second;
public Pair() { first = null; second = null; }
public Pair(T first, T second) { this.first = first; this.second = second; }
public T getFirst() { return first; }
public T getSecond() { return second; }
public void setFirst(T newValue) { first = newValue; }
public void setSecond(T newValue) { second = newValue; }
}
Pair
類引入了一個型別變數T,用尖括號括起來,並放在類名的後面。泛型類可以有多個型別變數:
public class Pair<T, U> {...}
類定義中的型別變數是指定方法的返回型別以及域和區域性變數的型別
//域
private T first;
//返回型別
public T getFirst() { return first; }
//區域性變數
public void setFirst(T newValue) { first = newValue; }
型別變數使用大寫形式,且比較短,這是很常見的,在Java庫中,使用變數E表示集合的元素型別,K
和V
分別表示表的關鍵字與值得型別。T
表示“任意型別”。
使用具體的型別代替型別變數就可以例項化泛型型別:
Pair<String>
可以將結果想象成帶有構造器的普通類:
Pair<String>()
Pair<String>(String, String)
和方法
String getFirst();
String getSecond();
void setFirst(String);
void setSecond(String);
泛型類可以看成是普通類的工廠
泛型方法
首先我們來看一個泛型方法的例項:
class ArrayAlg {
public static <T> T getMiddle(T...a){
return a[a.length / 2];
}
}
首先,這個方法是在普通類中定義的,而不是在泛型類中定義的。然而,這是一個泛型方法,可以從尖括號和型別變數看出這一點。注意,型別變數是放在修飾符的後面,返回型別的前面。
泛型方法可以定義在普通類中,也可以定義在泛型類中。
當呼叫一個泛型方法時,在方法名前的尖括號中放入具體的型別:
String middle = ArrayAlg.<String>getMiddle("a","b","c");
在這種情況下,方法呼叫中可以省略<String>
型別引數,編譯器會使用型別推斷來推斷出所呼叫的方法,也就是說可以這麼寫:
String middle = ArrayAlg.getMiddle("a","b","c");
公眾號
掃碼或微信搜尋 Vi的技術部落格,關注公眾號,不定期送書活動各種福利~