1. 泛型程序設計
泛型是Java程序設計中一個重要的思想,它可以被用在類、接口、方法中。泛型簡單來說就是:
1)所編寫的代碼在不用修改的前提下,可以被多種不同類型的對象所重用。
2)相較於雜亂的使用Object變量,泛型機制編寫的程序具有更好的安全性和可讀性,在效率上也會有所提升。
Java在JDK 1.5 之後增加了泛型機制,我們所熟知的 ArrayList 類就是泛型程序設計的一個典型例子。
1.1 使用泛型類的例子:ArrayList
ArrayList<String> stringList = new ArrayList<String>();
上述代碼中的 String
就是類型參數,它指明瞭 ArrayList 中存儲的數據類型爲 String
類型。也就是說,對於 stringList 而言,我們只能往裏面添加 String
類型的對象。
String str = "";
Integer a = 0;
stringList.add(str); // ok
stringList.add(a); // error
如上述例子所示,當我們向 stringList 添加其它類型的對象時,就會產生錯誤。
需要注意的是,類型參數不能是八大基本類型,如果的確需要用到它們,可以用其包裝類型替代(如用 Integer 代替int)。
JDK 1.7 之後,構造函數中的類型參數可以被省略,省略的類型可以從變量的類型推斷得出:
1.2 類型變量
在構建泛型類、泛型接口和泛型方法時,可以通過類型變量來“泛型化”數據。例如:
public class Pair<T> {
}
Pair<T>
中的 T 就是類型變量,被放在泛型類類名後面的尖括號裏。
關於類型變量,在Java庫中,常使用E、K、V、T等大寫字母表示:
E: 表示集合的元素類型
K:表示關鍵字
V:表示值(常與K對應)
T:表示“任意類型”(需要時還可以用鄰近的字母U和S)
類型變量可以定義數據域的類型、方法的參數類型以及返回值的類型。
2. 泛型類
利用類型變量,可以構建自己的泛型類。
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 定義了數據域的類型、方法的參數類型以及返回值的類型。在使用到Pair類時,只需要在實例化對象時用具體的類型參數替換類型變量即可。
泛型的實現利用了Java虛擬機的類型擦除機制。爲便於理解,可以將類型變量T理解成“某種變量”,在實例化泛型類時需要具體指明其類型,並用指定的類型“替換”它,以得到一個“具體的”對象。
3. 泛型接口
同樣的,我們可以構建自己的泛型接口。這裏需要注意的是, 接口中的數據域都是public static final(靜態常量),因此不能用類型變量定義。
這並不難理解,對於靜態常量而言,它的值在類或接口建立的時候就是確定的,既然如此,它肯定不能是個“類型不確定”的變量(類型都不能確定,它的值還怎麼定下來呢?)。
public interface Person<T> {
T information; // error
T getInformation(); // ok
}
對於泛型接口的實現,可以在實現時就指定類型變量的具體類型,這樣實現的類就是一個具體的類,而不是泛型類,如:
public class MyPerson implements Person<String> {
String information;
@Override
public String getInformation() {
return information;
}
}
當然也可以在實現此接口時不具體指定類型變量的具體類型,這樣得到的就是一個泛型類,如:
public class MyPerson<T> implements Person<T> {
T information;
@Override
public T getInformation() {
return information;
}
}
4. 泛型方法
除了泛型類和泛型接口,實際上,還可以在普通類中定義泛型方法(帶有類型參數的方法),例如:
public class MyPerson {
public static <T> T getFirstInformation(T[] information) {
if (information.length >= 2) {
return information[1];
}
return null;
}
}
從尖括號和類型變量可以看出,這是一個泛型方法。當調用泛型方法時,需要在方法名之前的尖括號中填入具體的類型,如:
String[] information = {"111111", "ABCDD", "boy"};
String name = MyPerson.<String>getFirstInformation(information);
System.out.println(name);
// 程序打印:ABCDD
需要注意的是,在定義時尖括號被放在了修飾符之後,返回類型之前;在調用時尖括號被放在了方法名之前。這樣定義的好處是可以避免語法分析的歧義。
試想如果尖括號被放在方法名之後,如方法: f<T, U>();
在調用方法時,對於g(f<a,b>(c)),是理解成函數f<a,b>(c)的返回值,還是兩個布爾變量f<a和b>(c)呢?