从当前版本 (Java 12) 开始,原始类型无法用 Java 泛型表示。更具体地说,我们不能提供原始类型作为类型参数。 (我们不能这样做,例如Foo<int>。)我们也不能使用类型变量作为new 表达式中的类型,所以我们不能使用new T[n] 来创建数组。因此,没有理想的方法来做到这一点。
可以使用一些反射 (java.lang.reflect.Array) 合理地做到这一点,但我们需要提供一个 Class 作为参数。这是一个如何完成的示例:
/**
* Unboxes a List in to a primitive array.
*
* @param list the List to convert to a primitive array
* @param arrayType the primitive array type to convert to
* @param <P> the primitive array type to convert to
* @return an array of P with the elements of the specified List
* @throws NullPointerException
* if either of the arguments are null, or if any of the elements
* of the List are null
* @throws IllegalArgumentException
* if the specified Class does not represent an array type, if
* the component type of the specified Class is not a primitive
* type, or if the elements of the specified List can not be
* stored in an array of type P
*/
public static <P> P toPrimitiveArray(List<?> list, Class<P> arrayType) {
if (!arrayType.isArray()) {
throw new IllegalArgumentException(arrayType.toString());
}
Class<?> primitiveType = arrayType.getComponentType();
if (!primitiveType.isPrimitive()) {
throw new IllegalArgumentException(primitiveType.toString());
}
P array = arrayType.cast(Array.newInstance(primitiveType, list.size()));
for (int i = 0; i < list.size(); i++) {
Array.set(array, i, list.get(i));
}
return array;
}
调用示例:
List<Integer> list = List.of(1, 2, 3);
int[] ints = toPrimitiveArray(list, int[].class);
请注意,Array.set 将执行加宽基元转换,因此以下工作:
List<Integer> list = List.of(1, 2, 3);
double[] doubles = toPrimitiveArray(list, double[].class);
但它不会进行窄化转换,所以下面会抛出异常:
List<Integer> list = List.of(1, 2, 3);
byte[] bytes = toPrimitiveArray(list, byte[].class); // throws
如果您愿意,也可以使用该代码使复制更容易:
public static int[] toIntArray(List<Integer> list) {
return toPrimitiveArray(list, int[].class);
}
public static double[] toDoubleArray(List<Double> list) {
return toPrimitiveArray(list, double[].class);
}
...
(不过,拥有多个这样的方法并不是真正的通用。)
您有时会看到地点的一个解决方案如下所示:
public static <P> P toPrimitiveArray(List<?> list) {
Object obj0 = list.get(0);
Class<?> type;
// "unbox" the Class of obj0
if (obj0 instanceof Integer)
type = int.class;
else if (obj0 instanceof Double)
type = double.class;
else if (...)
type = ...;
else
throw new IllegalArgumentException();
Object array = Array.newInstance(type, list.size());
for (int i = 0; i < list.size(); i++) {
Array.set(array, i, list.get(i));
}
return (P) array;
}
不过,这会带来很多问题:
最好只传入 Class 作为参数。
另外,虽然可以编写许多重载来拆箱数组:
public static int[] unbox(Integer[] arr) {...}
public static long[] unbox(Long[] arr) {...}
public static double[] unbox(Double[] arr) {...}
...
由于type erasure 的影响,不可能编写将许多不同类型的List 拆箱的重载,如下所示:
public static int[] unbox(List<Integer> list) {...}
public static long[] unbox(List<Long> list) {...}
public static double[] unbox(List<Double> list) {...}
...
这不会编译,因为我们不允许在同一个类中拥有多个具有相同名称和擦除的方法。这些方法必须有不同的名称。
附带说明,这里有一些非通用解决方案:
-
从 Java 8 开始,我们可以使用 Stream API 将 Lists 的 Integer、Long 和 Double 拆箱:
List<Long> list = List.of(1L, 2L, 3L);
long[] longs = list.stream().mapToLong(Long::longValue).toArray();
-
Google Guava 在其com.google.common.primitives 类中有Collection 拆箱方法,例如Doubles.toArray:
List<Double> list = List.of(1.0, 2.0, 3.0);
double[] doubles = Doubles.toArray(list);