2017-05-27 60 views
2

我有了這個通用類,具有方法返回一個通用陣列:通用陣列轉換異常

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 ????

任何人都可以解釋我爲什麼嗎?

我試圖應用盡可能多的這How to create a generic array,無濟於事。 我有一個醜陋的解決方法:

return Arrays.asList(txtTags.getItems()).toArray(new clTag[0]) 

但我想有它然後ZTagFieldClass。

+0

請張貼堆棧跟蹤。 – Turing85

+0

所有好的3方法。由於返回一個List而不是一個數組會更容易,我將首先回到設計中,看看爲什麼我設計這個類(很久以前)返回一個數組而不是List。謝謝。 – lvr123

回答

3

按設計工作:在運行時沒有通用類型。

它被擦除,並創建一個Object數組。一個Object數組不能轉換爲另一種數組。

這是Java泛型的一個限制,基於它們如何實現的方式。

這就是建議您小心使用數組和泛型。您可能更喜歡使用通用List。

只是要清楚這一點:是的,你可以讓數組使用泛型,正如其他答案所示。但是:你花時間去對抗症狀。 Java中的數組和泛型不能很好地結合在一起。接受,使用通用列表,從而:解決問題而不是解決問題。

+0

簡短而有效。這通常足夠:) – davidxxx

+0

謝謝。我同意你的評估。另一個答案是啓發! – GhostCat

+0

創建一個Object數組的事實與刪除或泛型沒有任何關係,而只是基於他傳入的方法調用的方法的已記錄運行時語義。 – newacct

4

數組已實現。這意味着它們持有對其組件類型的引用,並且在插入元素時,它會使用該引用來檢查插入的元素是否實際上是組件類型的子類型。

正因爲如此,創建一個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[]

public clTag[] getSelectedTags() { 
    return (clTag[]) txtTags.getItems(); 
} 

但你不能施放Object[]到一個clTag[],就像你不能投ObjectclTag

你的變通辦法,因爲你實際上提供給組件類型的引用:

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); 
} 
2

編譯後,類型將被刪除。
由於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()]); 

但數組不爲列表類型的工作。
特定類型的數組不能轉換爲其他類型的數組,即使它包含的元素是類型轉換的目標類型。
因此,即使數組包含具有此特定類型的元素(例如,),也不能將Object的數組轉換爲特定類型的數組。

因此,這將產生一個ClassCastException:

clTag[] values = (clTag[]) arrayObject; 

解決你的問題:


如果你可以使用Java 8,使用功能界面確實是一個乾淨的解決方案。 Jorn Vernee給出了一個非常好的答案來說明這一點。

否則,爪哇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; 
    } 
+0

這也是一個啓發。 – GhostCat

+0

沒問題。你激勵我進一步提高我的答案。現在讓我們來看看這個運算器要說什麼。如果他再次回來:-) – GhostCat

+1

改善答案總是一件好事:)等待和確實:))3個答案是不同的,並處理問題的真正截然不同的方面:設計警告,在java 8之前,因爲java 8路。 – davidxxx