[英]Generic array cast exception
我有一個Generic類,有一個方法返回一個通用數組:
public class ZTagField<T> extends JTextPane {
public ZTagField(StringBasedFactory<T> factory) {
assert (factory != null);
this.factory = factory;
init();
}
public T[] getItems() {
...
T[] arrItems = (T[]) currentItems.toArray((T[])new Object[0]);
return arrItems;
}
而另一個使用它:
public class Xxx {
ZTagField<clTag> txtTags = null;
public Xxx() {
txtTags = new ZTagField<clTag>(createFactory());
}
public clTag[] getSelectedTags() {
return txtTags.getItems();
}
}
后面這個txtTags.getItems()
給了我一個異常:==> Exception [Object cannot be cast to [clTag
????
誰能解釋我為什么?
我試圖盡可能多地應用這個如何創建一個通用數組 ,但無濟於事。 我有一個丑陋的解決方法:
return Arrays.asList(txtTags.getItems()).toArray(new clTag[0])
但我想在ZTagFieldClass中使用它。
陣列具體化。 這意味着它們包含對其組件類型的引用,並且在插入元素時,它使用該引用來檢查inserted元素是否實際上是組件類型的子類型。
因此,要創建T[]
,需要對組件類型T
進行具體引用,並且由於泛型被擦除,因此通用 T
不計算。 (這也是為什么你不能直接創建T[]
像T[] arr = new T[]
。)
集合的toArray
方法通過讓用戶傳遞組件類型的數組來解決這個問題。 但是你試圖通過將你的Object[]
轉換為T[]
來欺騙這個,它實際上並沒有創建一個T
數組(具體T
的引用來自哪里?)。 如果沒有取消選中,這樣的演員會失敗,除非T
實際上是Object
。
這也是ClassCastException
來源。 您創建了一個Object[]
,因此組件類型是Object
,無論您將其轉換為T[]
,組件類型都保持Object
。 但是稍后,你知道你想要的實際組件類型( clTag
):
public clTag[] getSelectedTags() {
return txtTags.getItems();
}
所以編譯器會在這里向clTag[]
插入一個隱式clTag[]
:
public clTag[] getSelectedTags() {
return (clTag[]) txtTags.getItems();
}
但是你不能將一個Object[]
clTag[]
轉換為clTag[]
,就像你不能將一個Object
clTag
為clTag
。
您的解決方法有效,因為您實際上提供了對組件類型的引用:
Arrays.asList(txtTags.getItems()).toArray(new clTag[0]) // <-- 'clTag' here
比傳遞組件類型數組更現代的解決方案是將一個封裝數組構造函數的IntFuntion<T[]>
傳遞給方法:
public T[] getItems(IntFunction<T[]> arrCons) {
...
T[] arrItems = currentItems.toArray(arrCons.apply(0));
return arrItems;
}
...
txtTags.getItems(clTag[]::new);
但是你無法以某種方式傳遞組件類型,除非你切換到返回List<T>
(如GhostCat也建議的那樣)。 由於泛型不具體化,則可以創建一個List<T>
但無一組分型的引用:
public List<T> getItems() {
...
return new ArrayList<>(currentItems);
}
按設計工作:在運行時沒有泛型類型。
它被刪除,並創建一個Object數組。 Object數組不能轉換為另一種數組。
這是Java泛型的限制之一,基於它們的實現方式。
這是建議你小心使用數組和泛型。 您可能更喜歡使用通用列表。
而且要明確這一點:是的,你可以讓數組與泛型一起使用,正如其他答案所顯示的那樣。 但是:你花時間與症狀作斗爭。 數組和泛型在Java中不能很好地結合在一起。 接受,使用通用列表,因此:解決問題而不是解決它。
編譯后,類型將被刪除。
由於T
不受特定類型的限制,因此T
將被Object
替換。
所以這 :
T[] arrItems = (T[]) currentItems.toArray((T[])...);
return arrItems;
不會在運行時創建並返回類實例使用的特定類型的數組,但只會創建一個Object
數組。
此外,在Collection.toArray()
您無法傳遞任何數組( new T[]
),因為它無法創建通用數組 。
因此,如果要使用toArray()
方法,最終只能以這種方式傳遞Object
數組:
Object[] arrayObject = values.toArray(new Object[currentItems.size()]);
但是數組不能用作List類型。
特定類型的數組不能轉換為另一種類型的數組,即使它包含的元素是轉換目標的類型。
因此,即使數組包含具有此特定類型的元素,也不能將Object
數組Object
轉換為特定類型的數組。
所以這會產生一個ClassCastException:
clTag[] values = (clTag[]) arrayObject;
要解決您的問題:
如果您可以使用Java 8,那么使用功能界面確實是一個干凈的解決方案。 Jorn Vernee給出了一個非常好的答案來說明它。
否則,在Java 8之前,創建與泛型集合中使用的參數化類型相同類型的數組的單一方法是:
1)創建具有指定類型的新數組。
java.lang.reflect.Array.newInstance(Class clazz, int length)
方法允許創建指定類和長度的數組。
2)在通用類的實例中存儲聲明類型的類。 您可以通過在其構造函數中添加類參數來實現。
3)從通用集合的元素中填充它。
一種簡單的方法是使用<Object, Object> Object[] java.util.Arrays.copyOf(Object[] original, int newLength, Class<? extends Object[]> newType)
方法,但它首先必須有效使用toArray()
將集合轉換為數組,以便能夠將其傳遞給copyOf()
方法。
例如,使用通用List
,您可以編寫:
public class ZTagField<T> {
private class<T> clazz;
private List<T> list = new ArrayList<>();
public ZTagField (class<T> clazz){
this.clazz = clazz;
}
public T[] get() {
T[] array = (T[]) Array.newInstance(clazz, list.size());
Class<? extends Object[]> clazzArray = array.getClass();
array = (T[]) Arrays.copyOf(values.toArray(), values.size(), clazzArray);
return array;
}
}
它有效,但正如所說它沒有效果。
一個更有效的解決方案是迭代列表並在新數組中添加元素而不是使用Arrays.copyOf()
:
public T[] get() {
T[] array = (T[]) Array.newInstance(clazz, list.size());
for (int i = 0; i < values.size(); i++) {
array[i] = values.get(i);
}
return array;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.