用Java創建泛型通用數組
- java
1.簡介
我們可能希望將數組用作支持泛型的類或函數的一部分。由於Java處理泛型的方式,這可能很困難。
在本教程中,我們將了解對數組使用泛型的挑戰。然後,我們將創建一個通用數組的示例。
我們還將看看Java API在哪裡解決了類似的問題。
2.使用通用數組時的注意事項
數組與泛型之間的重要區別在於它們如何執行類型檢查。具體來說,數組在運行時存儲和檢查類型信息。但是,泛型在編譯時檢查類型錯誤,並且在運行時沒有類型信息。
Java的語法表明我們可能能夠創建一個新的通用數組:
T[] elements = new T[size];
但是,如果嘗試這樣做,則會出現編譯錯誤。
要了解原因,請考慮以下內容:
public <T> T[] getArray(int size) {
T[] genericArray = new T[size]; // suppose this is allowed
return genericArray;
}
當未綁定的通用類型T
解析為Object,
我們在運行時的方法將是:
public Object[] getArray(int size) {
Object[] genericArray = new Object[size];
return genericArray;
}
然後,如果我們調用方法並將結果存儲在String
數組中:
String[] myArray = getArray(5);
該代碼可以正常編譯,但在運行時失敗,並帶有ClassCastException
。這是因為我們剛剛將Object[]
分配給String[]
引用。具體來說,編譯器進行隱式強制轉換將無法將Object[]
轉換為我們所需的String[]
類型。
儘管我們不能直接初始化通用數組,但是如果調用代碼提供了準確的信息類型,仍然有可能實現等效操作。
3.創建一個通用數組
對於我們的示例,讓我們考慮一個有界堆棧數據結構MyStack
,其中容量固定為一定大小。另外,由於我們希望堆棧可以使用任何類型,因此合理的實現選擇將是通用數組。
首先,讓我們創建一個字段來存儲堆棧元素,這是類型E
的通用數組:
private E[] elements;
其次,讓我們添加一個構造函數:
public MyStack(Class<E> clazz, int capacity) {
elements = (E[]) Array.newInstance(clazz, capacity);
}
注意我們java.lang.reflect.Array#newInstance
初始化我們的通用數組,該數組需要兩個參數。第一個參數指定新數組中對象的類型。第二個參數指定要為數組創建多少空間。 Array#newInstance
的結果為Object
類型,我們需要將其E[]
以創建我們的通用數組。
我們還應該注意命名類型參數clazz
而不是class,
的慣例,這是Java中的保留字。
4.考慮ArrayList
4.1。使用ArrayList
數組
通常,使用通用ArrayList
代替通用數組會更容易。讓我們看看如何更改MyStack
以使用ArrayList
。
首先,讓我們創建一個字段來存儲元素:
private List<E> elements;
其次,在我們的堆棧構造函數中,我們可以使用初始容量ArrayList
elements = new ArrayList<>(capacity);
它使我們的類更加簡單,因為我們不必使用反射。另外,在創建堆棧時,我們不需要傳遞類文字。最後,由於我們可以設置ArrayList
的初始容量,因此可以獲得與數組相同的好處。
因此,僅在極少數情況下或與需要數組的外部庫建立接口時,才需要構造泛型數組。
4.2。 ArrayList
實現
有趣的是, ArrayList
本身是使用通用數組實現的。讓我們窺視ArrayList
看看如何。
首先,讓我們看一下列表元素字段:
transient Object[] elementData;
注意ArrayList
使用Object
作為元素類型。因為直到運行時才知道我們的通用類型,所以Object
用作任何類型的超類。
ArrayList
中的幾乎所有操作都可以使用此通用數組,因為除了一種方法toArray
之外,它們不需要向外界提供強類型數組。
5.從集合構建數組
5.1。 LinkedList示例
讓我們看一下在Java Collections API中使用通用數組的情況,我們將在一個集合中構建一個新的數組。
首先,讓我們使用類型參數String
LinkedList
並向其中添加項目:
List<String> items = new LinkedList();
items.add("first item");
items.add("second item");
其次,讓我們構建剛剛添加的項目的數組:
String[] itemsAsArray = items.toArray(new String[0]);
要構建我們的數組, List
。 toArray
方法需要一個輸入數組。它純粹使用此數組來獲取類型信息,以創建正確類型的返回數組。
在上面的示例中,我們使用new String[0]
作為輸入數組來構建結果String
數組。
5.2。 LinkedList.toArray
實現
讓我們LinkedList.toArray
,看看它如何在Java JDK中實現。
首先,讓我們看一下方法簽名:
public <T> T[] toArray(T[] a)
其次,讓我們看看在需要時如何創建新數組:
a = (T[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
請注意,它是如何利用Array#newInstance
來構建新數組的,就像前面的堆棧示例一樣。另外,請注意如何使用參數a
Array#newInstance.
提供類型。最後,將Array#newInstance
的結果強制轉換為T[]
創建通用數組。
六,結論
在本文中,我們首先研究了數組與泛型之間的區別,然後介紹了創建泛型數組的示例。然後,我們展示了使用ArrayList
可能比使用通用數組更容易。最後,我們還研究了Collections API中通用數組的使用。