1. 程式人生 > >第二十五條 列表優先於陣列

第二十五條 列表優先於陣列

陣列與泛型相比,兩個不同點。 1、陣列是協變的,意思是如果A是B的子型別,那麼陣列A[]就是陣列B[]的子型別;泛型則是不可變的,對於任意兩個不同的型別C和D,List<C> 即不是 List<D> 的子型別,也不是基型別,不管C是不是D的子類。如果你認為泛型是有缺陷的,錯了,有缺陷的是陣列。

Object[] objArray = new Long[10]; objArray[0] = "I do not fit in";

這段程式碼是合法的,意思就是編譯時不會報錯,能通過編譯。

List<Object> list = new ArrayList<Long>(); list.add("I do not fit in");

這段程式碼不合法,編譯通不過,直接報錯,更別說運行了。

上面兩種寫法,都是錯誤的,第一個即使編譯通過了,執行起來還是會報錯的,耽誤工夫;第二個則直接報錯。我們需要的就是儘可能早的暴露問題和錯誤,以便於修改。很明顯,第二種佔優勢。

2、陣列是具體化的,因此陣列會在執行時檢查型別,所以編譯期是免檢的,執行起來才發現不對勁,會丟擲異常。泛型則是通過擦除來實現的,意思就是在編譯期檢查型別,而執行時拋棄元素型別的資訊,意思是編譯能通過就好了,後期一律放行,因為已經檢查過了,執行時是不會出問題的。有一個例子大家可以自己寫意下,就是用泛型加上反射。List<String> strs = new ArrayList<String>(); 我們可以根據反射去拿strs的add()方法,然後根據反射加入一個 Long 型別的資料,然後列印 strs的toString()方法,會發現元素新增成功。這從側面說出執行時是沒檢查元素型別的,當然,此時如果用迭代器去便利元素,用String 型別接受元素,則在便利到 Long 型別資料時,會報錯。可以參考下ArrayList的原始碼,底層是Object[] array 的資料結構。

由於以上兩點,陣列和泛型不能混合使用。建立泛型陣列 引數化型別 型別引數的陣列 是非法的,舉例 new List<E>[] 、new List<String> 、 new E[] 都是不合法的,是不會編譯通過的。為什麼建立泛型陣列會失敗,是因為編譯期不知道它的型別,它不是安全型別。

List<String>[] strArray = new List<String> [1];//假設允許泛型陣列          List<Integer>[] intArray = Arrays.asList(42); // 穿件一個集合,包含一個值為 42 的元素 Object[] obj = strArray;// 多型技術,陣列是協變的     obj[0] = intList; //將intList存入了strArray中。  String str = strArray[0].get(0); // 此時明明是個 int 型別的

通過上述例子,我們會發現,如果泛型和陣列能混用,就可能會出現上面的錯誤,這是原始碼的bug,而java是比較嚴謹的;陣列和泛型,一個是執行時檢查,一個是編譯時檢查,通俗點就是可能會出現檢查錯位的情況,所用混用可能就會 出現問題,java原始碼直接禁止他們混用。由於泛型是不可具體化的,所以執行期間得到的資訊不會比編譯期間多,因為資訊被擦除了,T[] 是不安全的,虛擬機器根本不知道它是哪個物件,因此,對泛型使用可變引數列也是不安全的。但無限萬用字元居然是安全的,例如

List<?>[] stringLists=new List<?>[1]; 這種是合法的。我們知道 List<E>list = new ArrayList<>(); 是不合法的,但我們可以 List<Object> list = new ArrayList<Object>(); List<E> li = new ArrayList<E>(list); 我們可以先建立實體類,然後通過泛型技術轉換。 如果我們混合陣列和泛型,編譯器報錯了,那麼,趕快使用列表代替陣列。