【发布时间】:2012-02-28 04:54:32
【问题描述】:
我的目标是实现一个方法,它将任意数量的数组连接成一个具有它们共同超类型的数组,并返回结果(类型化)数组。我有两个实现。
第一个(这个不需要简化):
public static <T> T[] concatArrays(Class<T> type, T[]... arrays) {
int totalLen = 0;
for (T[] arr: arrays) {
totalLen += arr.length;
}
T[] all = (T[]) Array.newInstance(type, totalLen);
int copied = 0;
for (T[] arr: arrays) {
System.arraycopy(arr, 0, all, copied, arr.length);
copied += arr.length;
}
return all;
}
让我们创建一些数组:
Long[] l = { 1L, 2L, 3L };
Integer[] i = { 4, 5, 6 };
Double[] d = { 7., 8., 9. };
我们的方法被调用:
Number[] n = concatArrays(Number.class, l, i, d);
这可行并且是完全类型安全的(例如,concatArrays(Long.class, l, i, d) 是编译器错误),但如果没有必要指定 Number.class 有点烦人。所以我实现了以下方法(这是我要简化的方法):
public static <T> T[] arrayConcat(T[] arr0, T[]... rest) {
Class commonSuperclass = arr0.getClass().getComponentType();
int totalLen = arr0.length;
for (T[] arr: rest) {
totalLen += arr.length;
Class compClass = arr.getClass().getComponentType();
while (! commonSuperclass.isAssignableFrom(compClass)) {
if (compClass.isAssignableFrom(commonSuperclass)) {
commonSuperclass = compClass;
break;
}
commonSuperclass = commonSuperclass.getSuperclass();
compClass = compClass.getSuperclass();
}
}
T[] all = (T[]) Array.newInstance(commonSuperclass, totalLen);
int copied = arr0.length;
System.arraycopy(arr0, 0, all, 0, copied);
for (T[] arr: rest) {
System.arraycopy(arr, 0, all, copied, arr.length);
copied += arr.length;
}
return all;
}
从客户的角度来看,这更好用:
Number[] n = arrayConcat(l, i, d);
同样,编译器足够聪明,可以在Long[] all = arrayConcat(l, i, d) 上给出适当的错误。由于编译器能够识别此错误,很明显我正在运行时执行编译器能够在编译时执行的工作(确定给定数组的公共超类)。有没有什么方法可以在不使用基于反射的方法来确定数组创建步骤的通用超类的情况下实现我的方法?
我试过这种方法:
public static <T> T[] arrayConcat(T[]... arrays) {
int totalLen = 0;
for (T[] arr: arrays) {
totalLen += arrays.length;
}
Object[] all = new Object[totalLen];
int copied = 0;
for (T[] arr: arrays) {
System.arraycopy(arr, 0, all, copied, arr.length);
copied += arr.length;
}
return (T[]) all;
}
但这会在返回时引发 ClassCastException。显然new T[totalLen] 也出局了。有没有人有其他想法?
【问题讨论】:
-
我认为您不应该在这里进行强制转换,我可以理解将相同类型的数组放入一个大数组中,但不要混合多种类型。
-
考虑到这一点,我可以实现一个更简单的解决方案(例如,使用
Arrays.copyOf(arr0, totalLen)创建初始数组),但这会导致一个新问题:现在Number[] n = arrayConcat(new Long[] { 1L, 2L, 3L }, new Integer[] { 4, 5, 6 }, new Double[] { 7., 8., 9. });不是 编译时错误,但在运行时会导致 ArrayStoreException。 -
你试过 ClassName.
arrayConcat(l, i, d)。我认为您的 ClassCastException 来自于 T 被推断为 Long 而不是 Number 的事实。 -
@Amir 我假设您指的是this answer?正如我在上面的评论中提到的那样,这实际上失败了:
Number[] n = arrayConcat(new Long[] { 1L, 2L, 3L }, new Integer[] { 4, 5, 6 }, new Double[] { 7., 8., 9. });不是编译时错误,但它会在运行时导致 ArrayStoreException。