讀古今文學網 > 編寫高質量代碼:改善Java程序的151個建議 > 建議95:強制聲明泛型的實際類型 >

建議95:強制聲明泛型的實際類型

Arrays工具類有一個方法asList可以把一個變長參數或數組轉變為列表,但是它有一個缺點:它所生成的List長度是不可改變的,而這在我們的項目開發中有時會很不方便。如果你期望生成的列表長度是可變,那就需要自己來寫一個數組的工具類了,代碼如下:


class ArrayUtils{

//把一個變長參數轉變為列表,並且長度可變

public static<T>List<T>asList(T……t){

List<T>list=new ArrayList<T>();

Collections.addAll(list, t);

return list;

}

}


這很簡單,與Arrays.asList的調用方式相同,我們傳入一個泛型對象,然後返回相應的List,代碼如下:


public static void main(Stringargs){

//正常用法

List<String>list1=ArrayUtils.asList("A","B");

//參數為空

List list2=ArrayUtils.asList();

//參數為數字和字符串的混合

List list3=ArrayUtils.asList(1,2,3.1);

}


這裡有三個變量需要說明:

(1)變量list1

變量list1是一個常規用法,沒有任何問題,泛型實際的參數類型是String,返回的結果也就是一個容納String元素的List對象。

(2)變量list2

變量list2中容納的是什麼元素呢?我們無法從代碼中推斷出list2列表到底容納的是什麼元素(因為它傳遞的參數是空,編譯器也不知道泛型的實際參數類型是什麼),不過,編譯器會很「聰明」地推斷出最頂層類Object就是其泛型類型,也就是說list2的完整定義如下:


List<Object>list2=ArrayUtils.asList();


如此一來,編譯時就不會給出"unchecked"警告了。現在新的問題出現了:如果期望list2是一個Integer類型的列表,而不是Object列表,因為後續的邏輯會把Integer類型加入到list2中,那該如何處理呢?

強制類型轉化(把asList強制轉換成List<Integer>)?行不通,雖然Java的泛型是編譯擦除式的,但是List<Object>和List<Integer>沒有繼承關係,不能進行強制轉換。

重新聲明一個List<Integer>,然後讀取List<Object>元素,一個一個地向下轉型過去?麻煩,而且效率又低。

最好的解決方法是強制聲明泛型類型,代碼如下:


List<Integer>list2=ArrayUtils.<Integer>asList();


就這麼簡單,asList方法要求的是一個泛型參數,那我們就在輸入前定義這是一個Integer類型的參數,當然,輸出也是Integer類型的集合了。

(3)變量list3

變量list3有兩種類型的元素:整數類型和浮點類型,那它生成的List泛型化參數應該是什麼呢?是Integer和Float的父類Number?你太高看編譯器了,它不會如此推斷的,當它發現多個元素的實際類型不一致時就會直接確認泛型類型是Object,而不會去追索元素類的公共父類是什麼,但是對於list3,我們更期望它的泛型參數是Number,都是數字嘛!參照list2變量,代碼修改如下:


List<Number>list3=ArrayUtils.<Number>asList(1,2,3.1);


Number是Integer和Float的父類,先把三個輸入參數向上轉型為Number,那麼返回的結果也就是List<Number>類型了。

通過強制泛型參數類型,我們明確了泛型方法的輸入、輸出參數類型,問題是我們要在什麼時候明確泛型類型呢?一句話:無法從代碼中推斷出泛型類型的情況下,即可強制聲明泛型類型。