【问题标题】:Array concatenaton and type casting [duplicate]数组连接和类型转换[重复]
【发布时间】:2015-08-10 19:52:13
【问题描述】:

我有一个实用程序class

public class ArrayUtils {
    public static <T> T[] concat(T[]... arrays) {
        if(arrays == null) {
            return null;
        }
        int size = 0;
        for(T[] array : arrays) {
            size += array.length;
        }

        T[] concatenatedArray = (T[]) new Object[size];

        int i = 0;
        for(T[] array : arrays) {
            for(T item : array) {
                concatenatedArray[i++] = item;
            }
        }

        return concatenatedArray;
    }
}

当我测试concat时,它崩溃了:

public class ArrayUtilsTest extends TestCase {

    public void testConcat() throws Exception {
        Integer[] first = new Integer[]{1,2,3};
        Integer[] second = new Integer[]{4,5,6};
        Integer[] concat = ArrayUtils.concat(first, second);
        assertEquals(6, concat.length);
    }
}

带有消息

java.lang.ClassCastException: java.lang.Object[] 无法转换为 java.lang.Integer[]

我想这与泛型有关。你能提供使它工作的建议吗?此外,这个问题的背景会很棒。

谢谢!

【问题讨论】:

标签: java generics


【解决方案1】:

公共类 ArrayUtilsTest 扩展 TestCase {

public void testConcat() throws Exception {
    Integer[] first = new Integer[]{1,2,3};
    Integer[] second = new Integer[]{4,5,6};
//YOU need to cast the returned array from concat to an "Integer[]"
    Object[] a = ArrayUtils.concat(first, second);
    Integer[] concat = Arrays.copyOf(a, a.length, Integer[].class);

    assertEquals(6, concat.length);
}

}

【讨论】:

    【解决方案2】:

    您的问题是您在 concat 方法中创建了一个 Object 数组。

    代替

    T[] concatenatedArray = (T[]) new Object[size];
    

    使用

    T[] concatenatedArray = (T[]) Array.newInstance(arrays[0].getClass()
            .getComponentType(), size);
    

    更新:虽然上面可能会为您完成这项工作,但 newacct 在 cmets 中指出,使用它甚至更好/更安全

    T[] concatenatedArray = (T[]) Array.newInstance(arrays.getClass()
            .getComponentType().getComponentType(), size);
    

    这将允许您跳过大小检查并使用该方法,例如ArrayUtils.concat(new String[]{"foo"}, new Integer[]{42});(如果需要),否则会以 ArrayStoreException 失败。

    您可以忽略类型安全警告,因为您知道数组的类型为T,但您应该确保至少有一个数组。

    你可以这样做,例如通过将您的方法签名修改为

    public static <T> T[] concat(T[] array1, T[]... arrays);
    

    作为一个简短的解释:这与泛型类型在 java 中的实现方式有关,参见。 https://docs.oracle.com/javase/tutorial/java/generics/genMethods.html.

    实际上,您在concat 中的第一次转换也应该失败(因为在这种情况下,您已经尝试将Object[] 转换为Integer[]),但是由于类型擦除,字节码将包含转换改为Object[](您应该能够通过查看字节码或使用反编译器来验证这一点)。

    另一方面,您的测试方法不是通用的,而是显式转换为Integer[](甚至在字节码中;)),所以它会失败。

    最后但并非最不重要的一般经验法则:如果可能,请尝试使用现有的库,例如 Apache Commons,而不是重新发明轮子。你会发现你的大部分问题在过去都已经被别人解决了;)

    【讨论】:

    • 谢谢。你介意解释一下为什么它不能以旧的方式工作吗?
    • @midnight 您实际上是在尝试这样做:Integer[] concatenatedArray = (Integer[]) new Object[size];,这是不可能的。 (Object 数组不一定是 Integer 数组)。如果您尝试在没有泛型的情况下执行此操作,则会出现编译器错误。
    • @midnight:我更新了我的答案。
    • 这通常不起作用。 arrays[0] 可以是运行时类U[],其中UT 的子类型,或者arrays[0] 可以是nil,或者arrays 可以为空。所以arrays[0].getClass().getComponentType() 并不总能得到你想要的。
    • @newacct:它适用于例如concat(Number[], Integer[])(这应该是你的第一种情况),如果任何数组是null(这应该是你的第二种情况),之前已经有一个NullPointerException,我建议修改签名以防范arrays.size() == 0 (这应该是你的第三种情况)。我错过了什么?
    【解决方案3】:

    您必须指定要为其创建数组的类

    T[] concatenatedArray = (T[])Array.newInstance(arrays[0][0].getClass(), size);
    

    这段代码将从T[]... arrays中获取第一个array,并获取第一个元素的类类型,用于创建Final Array

    【讨论】:

    • @marvin 代码更通用。
    • 这通常不起作用。 arrays[0][0] 可以是运行时类U,其中UT 的子类型,或者arrays[0][0] 可以是nil,或者arrays[0] 可以是nil 或空或arrays 可以为空。所以arrays[0][0].getClass() 并不总是有效。
    • 是的,我明白了,感谢您的洞察力。你的溶胶是最通用的。
    【解决方案4】:

    您可以从实际的arrays array-of-arrays 参数中获取T,如下所示:

    T[] concatenatedArray = (T[]) Array.newInstance(arrays.getClass()
        .getComponentType().getComponentType(), size);
    

    【讨论】:

      【解决方案5】:

      您可以使用 JRE 已经提供的功能来简化您的代码:

      public static <T> T[] concat(T[] first, T[]... arrays) {
          int size = first.length;
          for(T[] array : arrays) size += array.length;
          if(size==first.length) return first.clone();
          ArrayList<T> list=new ArrayList<>(size);
          Collections.addAll(list, first);
          for(T[] array: arrays) Collections.addAll(list, array);
          return list.toArray(first);
      }
      

      List 上调用toArray 以提供适当类型的数组,如果大小大于所提供数组的大小,将创建该类型的新数组。我们防止这种特殊情况,即大小不大于第一个数组,这意味着所有其他数组都是空的,方法是首先检查这种情况,然后简单地返回第一个数组的克隆。

      但在大多数情况下,您最好使用List 而不是处理数组……

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-09-03
        • 2011-03-17
        • 1970-01-01
        • 2016-06-12
        • 2014-08-30
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多