如何使用泛型方法将List <T>转换为Array t [](对于基本类型)?

Pau*_*ulo 3 java arrays generics collections generic-method

我正在使用泛型方法进行一些测试,我想在下面转换这两个方法(convertFloatListToArray和convertShortListToArray)(convertListToArray):

public class Helper{
    public static float[] convertFloatListToArray(List<Float> list){
        float[] array = new float[list.size()];

        for(int i = 0; i<list.size(); i++){
            array[i] = list.get(i);
        }

        return array;
    }

    public static short[] convertShortListToArray(List<Short> list){
        short[] array = new short[list.size()];

        for(int i = 0; i<list.size(); i++){
            array[i] = list.get(i);
        }

        return array;
    }
}
Run Code Online (Sandbox Code Playgroud)

但是当我尝试使用泛型时,如下所示,我有一些错误:

public class Helper{
    public static <T, E> T convertListToArray(List<E> list){
        T array = new T[list.size()];

        for(int i = 0; i<list.size(); i++){
            array[i] = list.get(i);
        }

        return array;
    }
}
Run Code Online (Sandbox Code Playgroud)

我可以理解java关于泛型的限制,但我想知道是否有人知道任何解决方案,使用泛型方法,我没有看到.

Rad*_*def 6

从当前版本(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;
}
Run Code Online (Sandbox Code Playgroud)

示例电话:

List<Integer> list = List.of(1, 2, 3);
int[] ints = toPrimitiveArray(list, int[].class);
Run Code Online (Sandbox Code Playgroud)

请注意,Array.set它将执行扩展的原始转换,因此以下工作原理:

List<Integer> list = List.of(1, 2, 3);
double[] doubles = toPrimitiveArray(list, double[].class);
Run Code Online (Sandbox Code Playgroud)

但它不会执行缩小转换,因此以下引发异常:

List<Integer> list = List.of(1, 2, 3);
byte[] bytes = toPrimitiveArray(list, byte[].class); // throws
Run Code Online (Sandbox Code Playgroud)

如果您愿意,该代码也可用于简化复制:

public static int[] toIntArray(List<Integer> list) {
    return toPrimitiveArray(list, int[].class);
}
public static double[] toDoubleArray(List<Double> list) {
    return toPrimitiveArray(list, double[].class);
}
...
Run Code Online (Sandbox Code Playgroud)

(但是,有这样的多种方法并不是通用的.)


您有时会看到的一个解决方案看起来像这样:

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;
}
Run Code Online (Sandbox Code Playgroud)

但是,有很多问题:

  • 如果列表为空,我们不知道要创建的数组类型.
  • 如果列表中有多种类型的对象,则不起作用.
  • 未经检查的结果数组的转换P,因此存在堆污染的危险.

传递Class一个参数更好.


此外,虽然可以编写许多unbox数组的重载:

public static int[]    unbox(Integer[] arr) {...}
public static long[]   unbox(Long[]    arr) {...}
public static double[] unbox(Double[]  arr) {...}
...
Run Code Online (Sandbox Code Playgroud)

由于类型擦除的影响,不可能编写卸载许多不同类型的重载,List如下所示:

public static int[]    unbox(List<Integer> list) {...}
public static long[]   unbox(List<Long>    list) {...}
public static double[] unbox(List<Double>  list) {...}
...
Run Code Online (Sandbox Code Playgroud)

这将无法编译,因为我们不允许在同一个类中具有多个具有相同名称和擦除的方法.这些方法必须有不同的名称.


作为附注,这里有一些非通用的解决方案: