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

Bil*_*son 5 java generics

我有以下泛型方法,返回一个通用数组:

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

但后来当我尝试使用以下结果获取结果数组时:

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

对于案例1,我总是在运行时遇到错误:

Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
Run Code Online (Sandbox Code Playgroud)

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

以下是我的理解,如果我错了,请纠正我.

正如Tim所提到的,类型擦除发生在编译时,因此在字节码中,每个T对象只是类型Object,同时,编译器会将对象的类型转换为"正确".

假设T是一个整数,其中声明了T,它是对象.对于它所引用的位置,它是T的类型转换(隐式).

除了声明T []数组,它是Object [],并且引用数组的地方,它仍然是Object [].没有隐式转换为T [].

Tim*_*sen 16

你所看到的解释是由于称为类型擦除的东西.以下是编译器执行类型擦除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;
}
Run Code Online (Sandbox Code Playgroud)

换句话说,此方法将返回一个类型的数组Object.没有办法转换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;
}
Run Code Online (Sandbox Code Playgroud)

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

LinkedList<Integer> list = new LinkedList<Integer>();    
Integer[] i = genericMethod(Integer.class, list);
Run Code Online (Sandbox Code Playgroud)

更新:

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

public static Object genericMethod2(List input) {
    return input.get(0);
}
Run Code Online (Sandbox Code Playgroud)

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

Integer j = genericMethod2(list);
Run Code Online (Sandbox Code Playgroud)

编译器将尝试将输出转换genericMethod2()Integer:

Integer j = (Integer)genericMethod2(list);
Run Code Online (Sandbox Code Playgroud)

这个演员是合法的,因为每一个Integer都是一个Object,而且它在这里成功,因为你传递了一个集合Integer.第二种方法与您为我们突出显示的第一种方法不同.

  • 好的,我会更新我的答案.顺便说一句,Tom Biegeleisen是我的兄弟. (2认同)