記得以前面試的時候曾被問過一個問題:數組和List的區別是什麼?當時答的無非就是效率,容量固定,List不能存基本類型等等。當Java發展到了1.5之後,出現了泛型版本的List,又爲這個問題的解答加入了一筆。下面就來講一下與這個話題相關的內容。 public static void main(String[] args) { Object[] array = new String[10]; array[0] = 10; } |
它是可以編譯通過的,因爲數組是協變的,Object[]類型的引用可以指向一個String[]類型的對象。但是運行的時候是會報出如下異常的:
Exception in thread "main" java.lang.ArrayStoreException: java.lang.Integer
原因就像呼吸一樣簡單。
但是對於泛型就不會出現這種情況了:
public static void main(String[] args) { List< Object> list = new ArrayList< String>(); list.add(10); }
這段代碼連編譯都不能通過。
2. 數組的具體化。
第二個要講的問題是數組是具體化的(reified),而泛型在運行時是被擦除的(erasure)。這句話的意思是數組是在運行時纔去判斷數組元素的類型約束,而泛型正好相反,在運行時,泛型的類型信息是會被擦除的,只有編譯的時候纔會對類型進行強化。
所以上面的例子中,數組的方法會在運行時報出ArrayStoreException,而泛型根本無法通過編譯。
3. 泛型與數組的結合。
這個問題的標題有些誤導,其實泛型與數組根本沒有任何結合的可能性。List<E>[]、List<Integer>[]、或者E[]的寫法都是錯誤的。因爲這些方法無法提供類型的安全性。我們來分別舉2個例子來說明它們會導致的錯誤。
public void testMethod1() { List< Integer>[] array = new List< Integer>[10]; List< String> list = new ArrayList< String>(); Object[] objs = array; objs[0] = list; }
這段代碼假設array這個泛型數組是可以被創建的,那麼它就可以被Object[]類型的變量引用,進而導致了array中的元素變成了List<String>類型了,發生了類型錯誤。
第二個例子是關於類型參數數組的:
public < E> void testMethod2(E e) { E[] array = new E[10]; Object[] objs = array; objs[0] = e; objs[1] = "string"; objs[2] = 10; objs[2] = true; }
與上邊的那個例子相似,array可以放入任何類型的元素了,這個是很不安全的。
這裏有一個例外的情況可以說一下,無限制通配符類型的泛型是可以創建泛型數組的。
List< ?>[] listArray = new List< ?>[10];