2017-05-27 28 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