如何转换List& T& gt;使用泛型方法将数组t [](对于基本类型)设置为? [英] How to convert List<T> to Array t[] (for primitive types) using generic-method?

查看:75
本文介绍了如何转换List& T& gt;使用泛型方法将数组t [](对于基本类型)设置为?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am doing some tests with generic-methods and I'd like to transform these two methods below (convertFloatListToArray and convertShortListToArray) in just one (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;
    }
}

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

But when I try to use generics, as below, I have some errors:

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;
    }
}

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

I can understand java limitations about generics, but I wonder if someone know any solution, using generic-method, that I am not seeing.

推荐答案

从当前版本(Java 12)开始,基本类型不能用Java泛型表示.更具体地说,我们不能提供原始类型作为类型实参.(我们不能执行 Foo< int> .)我们也不能将类型变量用作 new 表达式中的类型,因此我们不能执行 new T [n] 创建一个数组.因此,没有理想的方法来做到这一点.

As of the current version (Java 12), primitive types can't be represented with Java generics. More specifically, we can't provide a primitive type as a type argument. (We can't do e.g. Foo<int>.) We also can't use type variables as the type in a new expression, so we can't do new T[n] to create an array. Therefore, there's no ideal way to do this.

可以使用一些反射来合理地做到这一点(

It is possible to do this reasonably using some reflection (java.lang.reflect.Array), but we need to provide a Class as an argument. Here's an example of how it might be done:

/**
 * 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);

请注意

Note that Array.set will perform a widening primitive conversion, so the following works:

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

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

But it won't perform a narrowing conversion, so the following throws an exception:

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

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

If you wanted, that code could also be used to make duplication easier:

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

(不过,具有多种类似方法并不是真正的泛型.)

(Having multiple methods like that isn't really generic, though.)

您有时会看到地点的一种解决方案如下所示:

One solution that you'll sometimes see places looks something like this:

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;
}

尽管如此,它还是有很多问题:

There are a variety of problems with that, though:

  • We don't know what type of array to create if the list is empty.
  • Doesn't work if there's more than one type of object in the list.
  • Unchecked casting of the result array to P, so there's a danger of heap pollution.

最好只传入 Class 作为参数.

此外,尽管可以编写许多重载来取消数组装箱:

Also, while it's possible to just write many overloads which unbox arrays:

public static int[]    unbox(Integer[] arr) {...}
public static long[]   unbox(Long[]    arr) {...}
public static double[] unbox(Double[]  arr) {...}
...

由于类型擦除的影响,无法编写重载,该重载将许多不同类型的 List 拆箱,如下所示:

Because of the effects of type erasure, it's impossible to write overloads which unbox many different types of List, as in the following:

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

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

That won't compile, because we aren't allowed to have more than one method in the same class with the same name and erasure. The methods would have to have different names.

作为旁注,以下是一些非通用解决方案:

As a side-note, here are some non-generic solutions:

  • 从Java 8开始,我们可以使用以下命令取消对 Integer Long Double List 的装箱 Stream API:

  • As of Java 8 we can unbox Lists of Integer, Long and Double using the Stream API:

List<Long> list = List.of(1L, 2L, 3L);
long[] longs = list.stream().mapToLong(Long::longValue).toArray();

  • Google Guava在其

  • 这篇关于如何转换List&amp; T&amp; gt;使用泛型方法将数组t [](对于基本类型)设置为?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    相关文章
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆