Java从另一个数组的通用运行时类型创建数组

Java从另一个数组的通用运行时类型创建数组,java,arrays,generics,Java,Arrays,Generics,我有下面的代码,它合并了两个数组,并且应该处理除原语之外的任何类型 @SuppressWarnings("unchecked") public static synchronized <E> E[] aryMergeNoDup(E[]... arys){ HashSet<E> hs = new HashSet<E>(); for(E[] ary : arys) { if(ary == null) continu

我有下面的代码,它合并了两个数组,并且应该处理除原语之外的任何类型

@SuppressWarnings("unchecked")
public static synchronized <E> E[]  aryMergeNoDup(E[]... arys){
    HashSet<E> hs = new HashSet<E>();
        for(E[] ary : arys) {
            if(ary == null) continue;
            for(E item : ary) {
                if(item != null) hs.add(item);
            }
        }
        hs.remove(null);
        return hs.toArray((E[]) Array.newInstance(
                arys.getClass().getComponentType(),hs.size()));
}
变量
arys
的运行时类型为String[];但是,当我用
String.class
替换
arys.getClass().getComponentType()
时,代码运行良好

但是,该方法只能用于字符串,因为这样做。我看不出哪里出了问题,因为它们都应该引用
java.lang.String

AbstractCollection.java:188中引发异常的行是:

r[i] = (T)it.next();

public <T> T[] toArray(T[] a) {
    // Estimate size of array; be prepared to see more or fewer elements
    int size = size();
    T[] r = a.length >= size ? a :
              (T[])java.lang.reflect.Array
              .newInstance(a.getClass().getComponentType(), size);
    Iterator<E> it = iterator();

    for (int i = 0; i < r.length; i++) {
        if (! it.hasNext()) { // fewer elements than expected
            if (a != r)
                return Arrays.copyOf(r, i);
            r[i] = null; // null-terminate
            return r;
        }
        r[i] = (T)it.next();
    }
    return it.hasNext() ? finishToArray(r, it) : r;
}
r[i]=(T)it.next();
公共T[]toArray(T[]a){
//估计数组的大小;准备查看更多或更少的元素
int size=size();
T[]r=a.长度>=尺寸?a:
(T[])java.lang.reflect.Array
.newInstance(a.getClass().getComponentType(),大小);
迭代器it=迭代器();
对于(int i=0;i
我认为问题在于
arys
类似于数组的数组。因此,可能
arys.getClass().getComponentType()
String[]
,而不是
String

arys
的运行时类型是
E[]
,而不是
E[]
。因此,您可能需要
arys.getClass().getComponentType().getComponentType()
作为
Array.newInstance

的参数,这里有两个独立的东西在交互。首先,您使用的是varargs,它将其参数封装在动态生成的数组中。所以ARY的类型将是E[]],所以get组件的类型将是E[]。其次,泛型意味着E在运行时被擦除为Object,所以即使是两个
getComponentType
调用也不会将其删除,除非您对始终返回Object[]没有异议

您可以做的是使用arys[0]的组件类型(如果存在)。这甚至不适用于所有情况,因为例如,第二个数组的类型可能是超类,或者是第一个赋值的同级,与第一个类型的数组不兼容


为了解决这个问题,您可以通过检查所有数组的类型来计算最小上界类型,但是我认为如果您的典型用法是相同类型的数组,那么与“第一种类型赢”相比,这是过分的

arys.getClass().getComponentType().getComponentType()很管用!“其次,泛型意味着E在运行时被擦除为对象,所以即使两个getComponentType调用也不会删除它——除非您同意始终返回Object[]”。这没有意义。
arys
的类型是
E[][]
,这意味着
arys
的运行时类型,如果不是
null
,则保证是
E[][]
或子类型,除非您在某个地方做了不安全的事情。当然。我说的是“
E
在运行时被擦除为
Object
”,而不是“
E[][/code>在运行时被擦除为
Object
”。要点是,您将无法使用
arys.getComponentType().getComponentType()
来确定E的类型,而不是传递数组的运行时类型。确定,您是对的-varargs数组的运行时类型似乎是根据所传递对象的编译时类型选择的-我原以为它将始终是Object[]。因此
getComponentType().getComponentType()
将反映编译时的选择。当调用站点上的数组的编译时类型不等于其最派生的运行时类型时,使用元素的类型仍将选择更具体的数组类型。
r[i] = (T)it.next();

public <T> T[] toArray(T[] a) {
    // Estimate size of array; be prepared to see more or fewer elements
    int size = size();
    T[] r = a.length >= size ? a :
              (T[])java.lang.reflect.Array
              .newInstance(a.getClass().getComponentType(), size);
    Iterator<E> it = iterator();

    for (int i = 0; i < r.length; i++) {
        if (! it.hasNext()) { // fewer elements than expected
            if (a != r)
                return Arrays.copyOf(r, i);
            r[i] = null; // null-terminate
            return r;
        }
        r[i] = (T)it.next();
    }
    return it.hasNext() ? finishToArray(r, it) : r;
}