1.4 常用的泛型:使用泛型參數來編寫方法
Java5+
前面的小節介紹了泛型可以簡化Java代碼并使代碼能夠防范ClassCastException錯誤。除了作為JDK的一部分來使用泛型之外,還可以編寫你自己的泛型。當對類型相同的對象進行操作時泛型是很有用的,但是對象的具體類型直到對類實例化時才能知道。這種方式非常適合于包含關聯項目的集合或涉及查找的類。
下面編寫一個使用泛型參數的方法。回想一下前面是怎樣使用ArrayList類的—— 只在構造ArrayList時才指定它使用哪些對象類型。注意,在定義類時并不知道其類型,并且不能將java.lang.Object作為類型使用,因為最后將遇到類似以前的類型強制轉換問題。當定義泛型時,必須使用一種特殊的語言來代表類型。當聲明類名時要完成此操作。在下面的示例中,<T>表示一種類將使用的類型:
public class RandomSelection<T> { }
|
這里,類型指示符的尖括號看起來類似HTML語法,但實際上和HTML沒有關系,它們也不表示小于或大于!尖括號在一個泛型的類名與一個類型相結合的情況下使用,正如前面的ArrayList<Integer>那樣。盡管直到調用構造函數時才知道真實的類型,但我們可以在方法定義中使用替換類型。假定定義了一個叫做RandomSelection的類,該類使用另一個類的某個類型,暫時將該類型稱為T。但是,此類的名字仍是RandomSelection。另外,每次可以對多個類型執行這種操作,正如java.util.Map的定義所示的那樣。在這種情況下,在類名之后使用一個由逗號分隔的標識符列表即可。
public class MyGeneric<T,U,V> { }
|
上面定義的MyGeneric類涉及到三個類型,它們分別稱為T、U和V。下面編寫一個方法來擴展RandomSelection類,該方法將一個項目添加到一個由內部管理的泛型(類型為T)的ArrayList中:
public class RandomSelection<T> {
private ArrayList<T> list;
public RandomSelection() {
list = new ArrayList<T>();
}
public void add(T element) {
list.add(element);
}
}
|
注意,實際上并不是處理一個叫作T的類。T代表當某人創建RandomSelection的一個實例時使用的任意類。Java規范允許使用任意標識符,但是一般是使用單個大寫字母來和普通的類名進行區別。既然已經定義add方法接受一個類型T參數,則只能使用在構造RandomSelection實例時采用的相同的類型來調用此方法。以下的代碼是非法的并會產生一個編譯錯誤:
RandomSelection<String> rs = new RandomSelection<String>();
rs.add(new Date()); // illegal for a RandomSelection<String>
|
如果希望一個方法返回一個泛型類型,可以將方法簽名的返回類型設為T,正如下面的定義所示:
import java.util.Random;
public class RandomSelection<T> {
private java.util.Random random = new Random();
// ..... earlier methods omitted
public T getRandomElement() {
int index = random.nextInt(list.size());
return list.get(index);
}
}
|
getRandomElement方法返回一個類型T,即與在類聲明中定義的類型相同。通過構造一個類型實例,現在可以使用剛才定義的RandomSelection類:
RandomSelection<Integer> selector = new RandomSelection<Integer>();
selector.add(2);
selector.add(3);
selector.add(5);
selector.add(7);
selector.add(11);
Integer choice = selector.getRandomElement();
System.out.println(choice);
|
給一個整型變量choice賦值是安全的,因為selector的getRandomElement方法返回的總是一個Integer。情況確實如此,因為是使用Integer作為泛型類型來構造的selector實例。Add和getRandomElement方法的定義具有和構造函數的定義相同的類型,并且編譯器將會強制執行此約束。嘗試在構造函數中以一個不同的類型來使用RandomSelection類,這次使用前面定義的Fruit enum類:
RandomSelection<Fruit> fruitSelector = new RandomSelection<Fruit>();
fruitSelector.add(Fruit.APPLE);
fruitSelector.add(Fruit.ORANGE);
fruitSelector.add(Fruit.GRAPEFRUIT);
fruitSelector.add(Fruit.BANANA);
fruitSelector.add(Fruit.DURIAN);
Fruit fruitChoice = fruitSelector.getRandomElement();
System.out.println(fruitChoice);
|
可以看出,能夠直接使用來自getRandomElement方法的Fruit返回值,正如前面對待Integer那樣。如果你想要一個類與某種類型(直到構造該類時才知道具體的類型)的對象協同操作以及希望編譯器嚴格執行類型限制,那么你可以定義自己的泛型。這樣做的主要優點體現在它的安全和便利性上。若想了解有關泛型的更多信息,請查閱網址http:// java.sun.com/j2se/1.5.0/docs/guide/language/generics.html上的Generics Tutorial(泛型指南) 和Java 5文檔。