上一個建議指出了asList方法在轉換基本類型數組時存在的問題,接著我們看一下asList方法返回的列表有何特殊的地方,代碼如下所示:
enum Week{Sun, Mon, Tue, Wed, Thu, Fri, Sat}
public static void main(Stringargs){
//五天工作制
WeekworkDays={Week.Mon, Week.Tue, Week.Wed, Week.Thu, Week.Fri};
//轉換為列表
List<Week>list=Arrays.asList(workDays);
//增加週六也為工作日
list.add(Week.Sat);
/*工作日開始幹活了*/
}
很簡單的程序呀,默認聲明的工作日(workDays)是從週一到週五,偶爾週六也會算作工作日加入到工作日列表中。不過,這段程序執行時會不會有什麼問題呢?
編譯沒有任何問題,但是一運行,卻出現了如下結果:
Exception in thread"main"java.lang.UnsupportedOperationException
at java.util.AbstractList.add(AbstractList.java:131)
at java.util.AbstractList.add(AbstractList.java:91)
UnsupportedOperationException,不支持的操作?居然不支持List的add方法,這真是奇怪了!還是來追根尋源,看看asList方法的源代碼:
public static<T>List<T>asList(T……a){
return new ArrayList<T>(a);
}
直接new了一個ArrayList對像返回,難道ArrayList不支持add方法?不可能呀!可能,問題就出在這個ArrayList類上,此ArrayList非java.util.ArrayList,而是Arrays工具類的一個內置類,其構造函數如下所示:
//這是一個靜態私有內部類
private static class ArrayList<E>extends AbstractList<E>
implements RandomAccess, java.io.Serializable{
//存儲列表元素的數組
private final Ea;
//唯一的構造函數
ArrayList(Earray){
if(array==null)
throw new NullPointerException();
a=array;
}
/*其他方法省略*/
}
這裡的ArrayList是一個靜態私有內部類,除了Arrays能訪問外,其他類都不能訪問。仔細看這個類,它沒有提供add方法,那肯定是父類AbstractList提供了,來看代碼:
public boolean add(E e){
throw new UnsupportedOperationException();
}
父類確實提供了,但沒有提供具體的實現(源代碼上是通過add方法調用add(int, E)方法來實現的,為了便於講解,此處縮減了代碼),所以每個子類都需要自己覆寫add方法,而Arrays的內部類ArrayList沒有覆寫,因此add一個元素就會報錯了。
我們再深入地看看這個ArrayList靜態內部類,它僅僅實現了5個方法:
size:元素數量。
toArray:轉化為數組,實現了數組的淺拷貝。
get:獲得指定元素。
set:重置某一元素值。
contains:是否包含某元素。
對於我們經常使用的List.add和List.remove方法它都沒有實現,也就是說asList返回的是一個長度不可變的列表,數組是多長,轉換成的列表也就是多長,換句話說此處的列表只是數組的一個外殼,不再保持列表動態變長的特性,這才是我們要關注的重點(雖然此處JDK的設計有悖OO設計原則,但這不在我們討論的範圍內,而且我們也無力回天)。
有些開發者特別喜歡通過如下方式定義和初始化列表:
List<String>names=Arrays.asList("張三","李四","王五");
一句話完成了列表的定義和初始化,看似很便捷,卻深藏著重大隱患——列表長度無法修改。想想看,如果這樣一個List傳遞到一個允許add操作的方法中,那將會產生何種結果?如果讀者有這種習慣,請慎之戒之,除非非常自信該Lis只用於讀操作。