摘要:總結數組與泛型的關系還是有點復雜的,中不允許直接創建泛型數組。本文分析了其中原因并且總結了一些創建泛型數組的方式。
簡介
上一篇文章介紹了泛型的基本用法以及類型擦除的問題,現在來看看泛型和數組的關系。數組相比于Java 類庫中的容器類是比較特殊的,主要體現在三個方面:
數組創建后大小便固定,但效率更高
數組能追蹤它內部保存的元素的具體類型,插入的元素類型會在編譯期得到檢查
數組可以持有原始類型 ( int,float等 ),不過有了自動裝箱,容器類看上去也能持有原始類型了
那么當數組遇到泛型會怎樣? 能否創建泛型數組呢?這是這篇文章的主要內容。
這個系列的另外兩篇文章:
Java 泛型總結(一):基本用法與類型擦除
Java 泛型總結(三):通配符的使用
泛型數組 如何創建泛型數組如果有一個類如下:
class Generic{ }
如果要創建一個泛型數組,應該是這樣: Generic
那么如果要使用泛型數組怎么辦?一種方案是使用 ArrayList,比如下面的例子:
public class ListOfGenerics{ private List array = new ArrayList (); public void add(T item) { array.add(item); } public T get(int index) { return array.get(index); } }
如何創建真正的泛型數組呢?我們不能直接創建,但可以定義泛型數組的引用。比如:
public class ArrayOfGenericReference { static Generic[] gia; }
gia 是一個指向泛型數組的引用,這段代碼可以通過編譯。但是,我們并不能創建這個確切類型的數組,也就是不能使用 new Generic
public class ArrayOfGeneric { static final int SIZE = 100; static Generic[] gia; @SuppressWarnings("unchecked") public static void main(String[] args) { // Compiles; produces ClassCastException: //! gia = (Generic [])new Object[SIZE]; // Runtime type is the raw (erased) type: gia = (Generic [])new Generic[SIZE]; System.out.println(gia.getClass().getSimpleName()); gia[0] = new Generic (); //! gia[1] = new Object(); // Compile-time error // Discovers type mismatch at compile time: //! gia[2] = new Generic (); Generic g = gia[0]; } } /*輸出: Generic[] *///:~
數組能追蹤元素的實際類型,這個類型是在數組創建的時候建立的。上面被注釋掉的一行代碼: gia = (Generic
我個人的理解是:由于類型擦除,所以 Generic
上面的例子中,元素的類型是泛型類。下面看一個元素本身類型是泛型參數的例子:
public class GenericArray{ private T[] array; @SuppressWarnings("unchecked") public GenericArray(int sz) { array = (T[])new Object[sz]; // 創建泛型數組 } public void put(int index, T item) { array[index] = item; } public T get(int index) { return array[index]; } // Method that exposes the underlying representation: public T[] rep() { return array; } //返回數組 會報錯 public static void main(String[] args) { GenericArray gai = new GenericArray (10); // This causes a ClassCastException: //! Integer[] ia = gai.rep(); // This is OK: Object[] oa = gai.rep(); } }
在上面的代碼中,泛型數組的創建是創建一個 Object 數組,然后轉型為 T[]。但數組實際的類型還是 Object[]。在調用 rep()方法的時候,就報 ClassCastException 異常了,因為 Object[] 無法轉型為 Integer[]。
那創建泛型數組的代碼 array = (T[])new Object[sz] 為什么不會報錯呢?我的理解和前面介紹的類似,由于類型擦除,相當于轉型為 Object[],看上去就是沒轉,但是多了編譯器的參數檢查和自動轉型。而如果把泛型參數改成
public class GenericArray{ private T[] array; @SuppressWarnings("unchecked") public GenericArray(int sz) { array = (T[])new Object[sz]; // 創建泛型數組 } public void put(int index, T item) { array[index] = item; } public T get(int index) { return array[index]; } // Method that exposes the underlying representation: public T[] rep() { return array; } //返回數組 會報錯 public static void main(String[] args) { GenericArray gai = new GenericArray (10); // This causes a ClassCastException: //! Integer[] ia = gai.rep(); // This is OK: Object[] oa = gai.rep(); } }
相比于原始的版本,上面的代碼只修改了第一行,把
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer; at GenericArray.使用 Object[] array(GenericArray.java:15)
由于擦除,運行期的數組類型只能是 Object[],如果我們立即把它轉型為 T[],那么在編譯期就失去了數組的實際類型,編譯器也許無法發現潛在的錯誤。因此,更好的辦法是在內部最好使用 Object[] 數組,在取出元素的時候再轉型??聪旅娴睦樱?/p>
public class GenericArray2{ private Object[] array; public GenericArray2(int sz) { array = new Object[sz]; } public void put(int index, T item) { array[index] = item; } @SuppressWarnings("unchecked") public T get(int index) { return (T)array[index]; } @SuppressWarnings("unchecked") public T[] rep() { return (T[])array; // Warning: unchecked cast } public static void main(String[] args) { GenericArray2 gai = new GenericArray2 (10); for(int i = 0; i < 10; i ++) gai.put(i, i); for(int i = 0; i < 10; i ++) System.out.print(gai.get(i) + " "); System.out.println(); try { Integer[] ia = gai.rep(); } catch(Exception e) { System.out.println(e); } } } /* Output: (Sample) 0 1 2 3 4 5 6 7 8 9 java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer; *///:~
現在內部數組的呈現不是 T[] 而是 Object[],當 get() 被調用的時候數組的元素被轉型為 T,這正是元素的實際類型。不過調用 rep() 還是會報錯, 因為數組的實際類型依然是Object[],終究不能轉換為其它類型。使用 Object[] 代替 T[] 的好處是讓我們不會忘記數組運行期的實際類型,以至于不小心引入錯誤。
使用類型標識其實使用 Class 對象作為類型標識是更好的設計:
public class GenericArrayWithTypeToken{ private T[] array; @SuppressWarnings("unchecked") public GenericArrayWithTypeToken(Class type, int sz) { array = (T[])Array.newInstance(type, sz); } public void put(int index, T item) { array[index] = item; } public T get(int index) { return array[index]; } // Expose the underlying representation: public T[] rep() { return array; } public static void main(String[] args) { GenericArrayWithTypeToken gai = new GenericArrayWithTypeToken ( Integer.class, 10); // This now works: Integer[] ia = gai.rep(); } }
在構造器中傳入了 Class
數組與泛型的關系還是有點復雜的,Java 中不允許直接創建泛型數組。本文分析了其中原因并且總結了一些創建泛型數組的方式。其中有部分個人的理解,如果錯誤希望大家指正。下一篇會總結通配符的使用,有興趣的讀者可進入下一篇:Java 泛型總結(三):通配符的使用。
參考
Java 編程思想
如果我的文章對您有幫助,不妨點個贊支持一下(^_^)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/65967.html
摘要:然而中的泛型使用了類型擦除,所以只是偽泛型??偨Y本文介紹了泛型的使用,以及類型擦除相關的問題。一般情況下泛型的使用比較簡單,但是某些情況下,尤其是自己編寫使用泛型的類或者方法時要注意類型擦除的問題。 簡介 Java 在 1.5 引入了泛型機制,泛型本質是參數化類型,也就是說變量的類型是一個參數,在使用時再指定為具體類型。泛型可以用于類、接口、方法,通過使用泛型可以使代碼更簡單、安全。然...
簡介 前兩篇文章介紹了泛型的基本用法、類型擦除以及泛型數組。在泛型的使用中,還有個重要的東西叫通配符,本文介紹通配符的使用。 這個系列的另外兩篇文章: Java 泛型總結(一):基本用法與類型擦除 Java 泛型總結(二):泛型與數組 數組的協變 在了解通配符之前,先來了解一下數組。Java 中的數組是協變的,什么意思?看下面的例子: class Fruit {} class Apple ex...
摘要:提供給了用戶大量的語法糖,比如泛型自動裝箱拆箱循環變長參數內部類枚舉類斷言新特性方法引用等解語法糖語法糖的存在主要是方便開發人員使用。 首先,部分總結文字引用 簡書作者:Eric新之助 。鏈接:https://www.jianshu.com/p/4de08deb6ba4 已獲得授權 showImg(https://segmentfault.com/img/bVbfuX9?w=646&...
類型推斷 類型推斷是Java編譯器查看每個方法調用和相應聲明的能力,以確定使調用適用的類型參數,推理算法確定參數的類型,如果可用,還確定分配或返回結果的類型,最后,推理算法嘗試查找適用于所有參數的最具體類型。 為了說明最后一點,在下面的示例中,推斷確定傳遞給pick方法的第二個參數是Serializable類型: static T pick(T a1, T a2) { return a2; } ...
摘要:驗證過程驗證過程的目的是為了確保文件的字節流中包含的信息符合當前虛擬機的要求,并且不會危害虛擬機自身的安全。二虛擬機字節碼執行引擎虛擬機的執行引擎自行實現,可以自行制定指令集與執行引擎的結構體系。 本篇博客主要針對Java虛擬機的類加載機制,虛擬機字節碼執行引擎,早期編譯優化進行總結,其余部分總結請點擊Java虛擬總結上篇 。 一.虛擬機類加載機制 概述 虛擬機把描述類的數據從Clas...
閱讀 2222·2023-04-26 01:57
閱讀 3240·2023-04-25 16:30
閱讀 2325·2021-11-17 09:38
閱讀 1068·2021-10-08 10:14
閱讀 1383·2021-09-23 11:21
閱讀 3678·2019-08-29 17:28
閱讀 3450·2019-08-29 15:27
閱讀 944·2019-08-29 13:04