用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]);

要構建我們的數組, ListtoArray方法需要一個輸入數組。它純粹使用此數組來獲取類型信息,以創建正確類型的返回數組。

在上面的示例中,我們使用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中通用數組的使用。