如何在 Java 泛型方法中正确返回泛型数组?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/34939499/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 23:34:24  来源:igfitidea点击:

How to properly return generic array in Java generic method?

javagenerics

提问by Bill Randerson

I have below generic method that returns a generic array:

我有以下返回通用数组的通用方法:

public static <T> T[] genericMethod1(List<T> input) {
    T[] res = (T[]) new Object[input.size()];

    int i = 0;
    for (T t : input) {
        res[i] = t;
        i++;
    }
    return res;
}

public static <T> T genericMethod2(List<T> input) {
    return input.get(0);
}

But later when I try to get the result array with:

但是稍后当我尝试使用以下方法获取结果数组时:

LinkedList<Integer> list = new LinkedList<Integer>();
list.addFirst(1);
list.addFirst(1);

Integer[] i = (Integer[]) genericMethod1(list);  // 1) Runtime error
Integer j = genericMethod2(list);        // 2) works

For case 1, I always get error at runtime:

对于情况 1,我总是在运行时出错:

Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;

Anybody can explain why and how to return the generic array properly? Thanks.

任何人都可以解释为什么以及如何正确返回通用数组?谢谢。

Below is my understanding, please correct me if I'm wrong.

As Tim mentioned, type erasure happened at compile time, so in bytecode, each T object is just type Object, meanwhile, compiler will add type cast from Object to T "properly".

Say T is an Integer, where T is declared, it's Object. For where it's referred, it's type cast (implicitly) to T.

EXCEPT that if T[] array is declared, it's Object[], and where the array is referred, it stays Object[]. No implicit cast to T[] happens.

以下是我的理解,如有不对请指正。

正如 Tim 提到的,类型擦除发生在编译时,所以在字节码中,每个 T 对象只是类型 Object,同时,编译器会“正确地”添加从 Object 到 T 的类型转换。

假设 T 是一个整数,其中 T 被声明,它是对象。对于它被引用的地方,它是类型转换(隐式)到 T。

除了如果声明了 T[] 数组,它是 Object[],并且在引用数组的地方,它仍然是 Object[]。不会发生对 T[] 的隐式转换。

回答by Tim Biegeleisen

The explanation for what you are seeing is due to something called type erasure. Here is what your genericMethod()will look like afterthe compiler performs type erasure:

您所看到的解释是由于称为类型擦除的东西。以下是编译器执行类型擦除genericMethod()样子:

public static Object[] genericMethod(List input) {
    Object[] res = new Object[input.size()];

    int i = 0;
    for (Object t : input) {
        res[i] = t;
        i++;
    }
    return res;
}

In other words, this method will return an array of type Object. There is no way to cast an Object[]to an Integer[]because they are not the same type. If you want your method to be able to dynamically return the type you want, then you can use Array.newInstance(). This will require also passing in the type of the array you want as an input parameter:

换句话说,此方法将返回一个类型为 的数组Object。无法将 an 转换Object[]为 an ,Integer[]因为它们的类型不同。如果您希望您的方法能够动态返回您想要的类型,那么您可以使用Array.newInstance(). 这还需要传入您想要的数组类型作为输入参数:

public static <T> T[] genericMethod(Class<T> clazz, List<T> input) {
    @SuppressWarnings("unchecked")
    T[] res = (T[]) Array.newInstance(clazz, input.size());

    int i = 0;
    for (T t : input) {
        res[i] = t;
        i++;
    }
    return res;
}

Now your code snippet will run without error:

现在您的代码片段将无误地运行:

LinkedList<Integer> list = new LinkedList<Integer>();    
Integer[] i = genericMethod(Integer.class, list);

Update:

更新:

Your second method, genericMethod2(), will look like this after type erasure:

您的第二种方法 ,genericMethod2()将在类型擦除后如下所示:

public static Object genericMethod2(List input) {
    return input.get(0);
}

It will return the first element of the input list, cast to Object. Here is your usage of that method:

它将返回输入列表的第一个元素,强制转换为Object。这是您对该方法的使用:

Integer j = genericMethod2(list);

The compiler will try to cast the output from genericMethod2()to Integer:

编译器将尝试将输出从genericMethod2()to 转换为Integer

Integer j = (Integer)genericMethod2(list);

This cast is legal, because every Integeris also an Object, and furthermore it succeeds here because you passed in a collection of Integer. This second method is not the same scenario as the first one you highlighted for us.

这个转换是合法的,因为 everyInteger也是 an Object,而且它在这里成功是因为你传入了Integer. 第二种方法与您为我们强调的第一种方法不同。

回答by rai.skumar

When calling the method, genericMethodyou are assuming that it returns array of integers, which is NOT correct. It actually returns array of type Object at runtime.

调用该方法时,genericMethod您假设它返回整数数组,这是不正确的。它实际上在运行时返回 Object 类型的数组。

    List<Integer> input = new ArrayList<Integer>();
    input.add(1);
    Object[] output = genericMethod(input);
    for(Object obj : output){
        System.out.println("Value= "+ (Integer)obj);
    }

So we need to cast the individual content of the array.

所以我们需要转换数组的单个内容。

One general guidline is that we shouldn't mix ARRAY and GENERICS in Java.

一个通用准则是我们不应该在 Java 中混合使用 ARRAY 和 GENERICS。

Update:

更新:

Reference from Effective Java:

来自Effective Java 的参考:

In Summary, arrays and generics have very different type rules. Arrays are covariant and reified; generics are invariant and erased. As a consequcne, arrays provide runtime type safety but not compile-time type safety and vice versa for generics. Generally speaking, arrays and generics don't mix well. If you find yourself mixing them and getting compile-time error or warnings, your first impulse should be to replace the arrays with lists.

总之,数组和泛型具有非常不同的类型规则。数组是协变和具体化的;泛型是不变的并被擦除。因此,数组提供运行时类型安全,但不提供编译时类型安全,反之亦然。一般来说,数组和泛型不能很好地混合。如果您发现自己混合使用它们并收到编译时错误或警告,您的第一个冲动应该是用列表替换数组。

回答by hinneLinks

Another way is to do it like in java.util.ArrayList.toArray(T[]). You pass the type of Array to that Method, if it's big enough it will be reused, otherwise an new Array is generated.

另一种方法是像在java.util.ArrayList.toArray(T[]). 您将 Array 的类型传递给该方法,如果它足够大,它将被重用,否则会生成一个新的 Array。

Example:

例子:

    List<Integer> intList = new ArrayList<>();
    intList.add(Integer.valueOf(1));
    intList.add(Integer.valueOf(2));
    intList.add(Integer.valueOf(3));
    Integer[] array = intList.toArray(new Integer[] {});
    System.out.println(Arrays.toString(array));//Will Print [1, 2, 3]

Implementation of ArrayList.toArray(T[])see here.

执行ArrayList.toArray(T[])这里