Java 中是否可以使用数组的可变参数?
Is varargs of array possible in Java?
我尝试实现一种方法来合并任意数量的数组。
@SuppressWarnings("unchecked")
public static <T> T[] merge(T[]... arrs) {
int length = 0;
for (T[] arr : arrs) {
length += arr.length;
}
T[] merged = (T[]) new Object[length];
int destPos = 0;
for (T[] arr : arrs) {
System.arraycopy(arr, 0, merged, destPos, arr.length);
destPos += arr.length;
}
return merged;
}
编译通过,看起来没问题。然后我像这样测试了这个方法:
String[] a = {"a", "b", "c"};
String[] b = {"e", "f"};
String[] c = {"g", "h", "i"};
String[] m = merge(a,b,c);
虽然这段代码编译成功,但抛出异常:
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
at ntalbs.test.ArrayMerge.main(ArrayMerge.java:25)
代码看起来合理且编译成功,但不起作用并在运行时抛出异常。你能解释一下为什么这段代码不起作用吗?我错过了什么?
问题与可变参数无关。
如错误消息所述:您的方法创建 return 一个 Object[]
,然后您将其转换为 String[]
。但是 Object[]
不是 String[]
。因此例外。
查看 Collection 的源代码(及其 T[] toArray()
方法),了解它如何基本上完成您正在尝试做的事情:创建一个 T[]
类型的数组。
您可以通过 T 的 class 创建数组。由于您传入了对象,因此您可以从其中一个对象中获取类型。像这样:
Class<T> clazz = arrs[0].getClass();
T[] arr = (T[]) Array.newInstance(clazz.getComponentType(), <length of all passed arrays>);
然后填充数组。
你的问题不是因为可变参数。
是因为线,
T[] merged = (T[]) new Object[length];
无法将 Object
数组转换为 String
数组。
使用,
T[] merged = (T[])java.lang.reflect.Array.newInstance(
arrs[0].getClass().getComponentType(), length);
注:
正如@JB 所指出的,这仅在您尝试合并相同的数组类型时才有效。如果你想合并不同的数组类型,那么你必须找到所有数组共有的超类型并用它代替 arrs[0].getClass()
.
您不能将 Object[] 转换为 String[]。您需要创建正确运行时类型的实例。
为此,您可以使用 java.lang.reflect.Array#newInstance 方法。要获得正确的类型,您可以向合并方法添加一个 Class 参数,如下所示:
public static <T> T[] merge(final Class<T> arrayType, T[]... arrs) {
int length = 0;
for (T[] arr : arrs) {
length += arr.length;
}
@SuppressWarnings("unchecked")
T[] merged = (T[]) Array.newInstance(arrayType, length);
int destPos = 0;
for (T[] arr : arrs) {
System.arraycopy(arr, 0, merged, destPos, arr.length);
destPos += arr.length;
}
return merged;
}
这可以在没有运行时异常的情况下执行:
String[] a = { "a", "b", "c" };
String[] b = { "e", "f" };
String[] c = { "g", "h", "i" };
String[] m = merge(String.class, a, b, c);
Yes,varargs 适用于所有 Java 类型。以上异常是由行T[] merged = (T[]) new Object[length];
产生的,与varargs.
无关
数组并非设计为通用的。我建议改用 Collections 框架。
如果您无法更改代码结构,请尝试类似的操作:
@SuppressWarnings("unchecked")
public static <T> T[] merge(T[]... arrays) {
int fullSize = 0;
for (T[] array: arrays) {
fullSize += array.length;
}
List<T> concatenatedList = new ArrayList<T>(fullSize);
for (T[] array: arrays) {
concatenatedList.addAll(Arrays.asList(array));
}
Class<T> targetType = (Class<T>)arrays
.getClass().getComponentType().getComponentType()
;
return concatenatedList.toArray(
(T[])Array.newInstance(targetType, concatenatedList.size())
);
}
我尝试实现一种方法来合并任意数量的数组。
@SuppressWarnings("unchecked")
public static <T> T[] merge(T[]... arrs) {
int length = 0;
for (T[] arr : arrs) {
length += arr.length;
}
T[] merged = (T[]) new Object[length];
int destPos = 0;
for (T[] arr : arrs) {
System.arraycopy(arr, 0, merged, destPos, arr.length);
destPos += arr.length;
}
return merged;
}
编译通过,看起来没问题。然后我像这样测试了这个方法:
String[] a = {"a", "b", "c"};
String[] b = {"e", "f"};
String[] c = {"g", "h", "i"};
String[] m = merge(a,b,c);
虽然这段代码编译成功,但抛出异常:
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
at ntalbs.test.ArrayMerge.main(ArrayMerge.java:25)
代码看起来合理且编译成功,但不起作用并在运行时抛出异常。你能解释一下为什么这段代码不起作用吗?我错过了什么?
问题与可变参数无关。
如错误消息所述:您的方法创建 return 一个 Object[]
,然后您将其转换为 String[]
。但是 Object[]
不是 String[]
。因此例外。
查看 Collection 的源代码(及其 T[] toArray()
方法),了解它如何基本上完成您正在尝试做的事情:创建一个 T[]
类型的数组。
您可以通过 T 的 class 创建数组。由于您传入了对象,因此您可以从其中一个对象中获取类型。像这样:
Class<T> clazz = arrs[0].getClass();
T[] arr = (T[]) Array.newInstance(clazz.getComponentType(), <length of all passed arrays>);
然后填充数组。
你的问题不是因为可变参数。
是因为线,
T[] merged = (T[]) new Object[length];
无法将 Object
数组转换为 String
数组。
使用,
T[] merged = (T[])java.lang.reflect.Array.newInstance(
arrs[0].getClass().getComponentType(), length);
注:
正如@JB 所指出的,这仅在您尝试合并相同的数组类型时才有效。如果你想合并不同的数组类型,那么你必须找到所有数组共有的超类型并用它代替 arrs[0].getClass()
.
您不能将 Object[] 转换为 String[]。您需要创建正确运行时类型的实例。 为此,您可以使用 java.lang.reflect.Array#newInstance 方法。要获得正确的类型,您可以向合并方法添加一个 Class 参数,如下所示:
public static <T> T[] merge(final Class<T> arrayType, T[]... arrs) {
int length = 0;
for (T[] arr : arrs) {
length += arr.length;
}
@SuppressWarnings("unchecked")
T[] merged = (T[]) Array.newInstance(arrayType, length);
int destPos = 0;
for (T[] arr : arrs) {
System.arraycopy(arr, 0, merged, destPos, arr.length);
destPos += arr.length;
}
return merged;
}
这可以在没有运行时异常的情况下执行:
String[] a = { "a", "b", "c" };
String[] b = { "e", "f" };
String[] c = { "g", "h", "i" };
String[] m = merge(String.class, a, b, c);
Yes,varargs 适用于所有 Java 类型。以上异常是由行T[] merged = (T[]) new Object[length];
产生的,与varargs.
数组并非设计为通用的。我建议改用 Collections 框架。
如果您无法更改代码结构,请尝试类似的操作:
@SuppressWarnings("unchecked")
public static <T> T[] merge(T[]... arrays) {
int fullSize = 0;
for (T[] array: arrays) {
fullSize += array.length;
}
List<T> concatenatedList = new ArrayList<T>(fullSize);
for (T[] array: arrays) {
concatenatedList.addAll(Arrays.asList(array));
}
Class<T> targetType = (Class<T>)arrays
.getClass().getComponentType().getComponentType()
;
return concatenatedList.toArray(
(T[])Array.newInstance(targetType, concatenatedList.size())
);
}