【问题标题】:Java create array from generic runtime type of another arrayJava从另一个数组的通用运行时类型创建数组
【发布时间】:2013-02-13 17:14:53
【问题描述】:

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

@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()));
}

但是当代码运行时,它会产生这个异常:

java.lang.ArrayStoreException: java.lang.String
  at java.util.AbstractCollection.toArray(AbstractCollection.java:188)
  at util.Utils.aryMergeNoDup(Utils.java:197)

变量arys的运行时类型为String[];但是,当我将 arys.getClass().getComponentType() 替换为 String.class 时,代码运行良好。

但是,该方法只能用于字符串,因为 pf this.我看不出出了什么问题,因为他们都应该引用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;
}

【问题讨论】:

    标签: java arrays generics


    【解决方案1】:

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

    【讨论】:

    • arys.getClass().getComponentType().getComponentType() 是一种享受!
    【解决方案2】:

    我认为问题在于arys 类似于数组数组。所以arys.getClass().getComponentType() 可能是String[],而不是String

    【讨论】:

      【解决方案3】:

      你有两个不同的东西在这里交互。首先,您使用的是可变参数,它在幕后将它们的参数包装在一个动态生成的数组中。所以 arys 的类型将是 E[][],所以获取组件类型将是 E[]。其次,泛型意味着 E 在运行时被擦除为 Object,因此即使两次 getComponentType 调用也不会删除它 - 除非您可以始终返回 Object[]。

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

      要解决这个问题,您可以通过检查所有数组的类型来计算最小上限类型,但如果您的典型用法是相同类型的数组,我认为这与“第一种类型获胜”相比是多余的。

      【讨论】:

      • “第二,泛型意味着 E 在运行时被擦除为 Object,因此即使两次 getComponentType 调用也不会删除它 - 除非你可以始终返回 Object[]。”这没有意义。 arys的类型是E[][],也就是说arys的运行时类型,如果不是null,保证是E[][]或者子类型,除非你在某处做了不安全的事情。
      • 当然。我说“E 在运行时被擦除为Object”,而不是“E[][] 在运行时被擦除为Object”。重点是您将无法使用arys.getComponentType().getComponentType() 来确定 E 的类型,而不是传递数组的运行时类型。
      • 好的,你是对的——可变参数数组的运行时类型似乎是根据传递对象的编译时类型选择的——我原以为它总是对象[][]。所以getComponentType().getComponentType() 将反映编译时的选择。当调用站点的数组的编译时类型不等于它们最派生的运行时类型时,使用元素的类型仍然会选择更具体的数组类型。
      猜你喜欢
      • 1970-01-01
      • 2013-02-01
      • 2011-05-25
      • 2012-12-16
      • 2017-05-06
      • 2013-08-21
      • 2011-10-27
      • 2021-09-27
      相关资源
      最近更新 更多