Java方法泛型返回数组的问题

问题

用Java写一个类似Python中random.choice 功能的方法时碰到了一个令我费解的情况。

源代码如下:

RandomUtil.java

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;

public class RandomUtil {

    public static final Random random = new Random();

    public static <T> T[] choice(T[] arr, int n) {
        List<T> result = new ArrayList<T>();

        while(result.size() < n) {
            int i = random.nextInt(n);

            if(!result.contains(arr[i])) {
                result.add(arr[i]);
            }
        }

        return (T[]) result.toArray();
    }
}

Test.java
import java.util.Arrays;

public class Test {

    public static void main(String[] args) {
        Integer[] array = {1, 2, 3, 4};
        Integer[] res = RandomUtil.choice(array, 2);
        System.out.println(Arrays.toString(res));
    }
}

看上去好像没什么问题,编译通过,除了提示有未经检验的类型转换。然而运行的时候抛出了ClassCastException 异常。关键是,异常居然指向Test.java第七行:

 

RandomUtil.choice 方法使用了泛型,接受一个任意类型的数组,然后从数组中随机抽出一定个数的元素,添加到相同类型的List中,最后将List转换成数组返回。

main函数中调用RandomUtil.choice 时传入了Integer 类型的数组,理论上T就代表Integer 类型,result 为List<Integer> ,返回时调用ArrayList.toArray() 方法返回Object 数组,然后强转为Integer[] 最后返回。

也就是说,RandomUtil.java的20行将Object[] 数组强制转化成Integer[] 成功并返回了,但到了把返回值赋值给同样时Integer[] 类型的变量时就不认账了。。。根据报错信息,返回值又变成了Object[] 。真是摸不着头。。。

 

PS: JDK1.6和JDK1.8均是如此。

 


打脸

简而言之,以下两点造成以上问题:

  1. 方法ArrayList.toArray() 返回Object[] 类型变量
  2. Java泛型擦除

无参数的ArrayList.toArray() 方法返回的是Object[] ,根本不能强制转换成String[] 。

之所以在20行没有抛出异常,是因为运行时泛型是擦除的。20行实际上什么也没做,运行时JVM并不知道T这个泛型参数是什么类型,而是以Object 替代,所以20行类型转换没有意义。而到了赋值到Integer[] 类型的变量上时,才发现了类型不匹配的问题,从而抛出异常。

 

修改

public static <T> T[] choice(T[] arr, int n) {
    Random random = new Random();
    List<T> result = new ArrayList<T>();
    while(result.size() < n) {
        int i = random.nextInt(n);
        if(!result.contains(arr[i])) {
            result.add(arr[i]);
        }
    }

    Object[] target = (Object[]) Array.newInstance(arr.getClass().getComponentType(), n);
    return (T[]) result.toArray(target);
}

 

 

 

相关文章:

https://stackoverflow.com/questions/36161096/generics-and-classcastexception

https://docs.oracle.com/javase/tutorial/java/generics/genTypes.html

https://blog.csdn.net/briblue/article/details/76736356