Java中的 List.toArray()方法的相关问题

1.为什么toArray()不能利用泛型?

我们在使用List的时候,明明给List传递了一个泛型参数,为什么toArray还是给我们返回了一个object数组?

首先我们要清楚的是,java中的数组也是类

Object[] newArray = new Object[]{"AAA","BBB"};
// 出现类型转换异常
// String[]q=(String[])(newArray);

// 这种就不会出现类型转换异常,这种是取出数组对象的一个元素了,与数组对象本身就没有关系了
// 因为数组对象内部存储的本来就是String,所以不会出现异常
String o = (String) newArray[0];

为什么会出现类型转换异常?因为newArray的实际类型是Object[],而不是String[],Object[]又不是String[]的子类,当然转换出错啊。虽然数组中的元素存储的是String类型,但是数组本身的实际类型并不是String[]类型,所以就会出现类型转换异常

在ArrayList中,存储元素使用的是一个Object类型的数组

transient Object[] elementData; 

所以,首先有一点我们已经清楚了,那就是即使有泛型,也不能让编译器在编译成字节码的时候在返回值上加上强制类型转换了,因为Object[]数组和String[]数组之间根本就不能进行强制类型转换,即使Object[]中存的全部是字符串,Java的泛型在编译过程中会被编译器处理,生成的字节码中,我们的字节码中的code部分上的泛型已经被擦除了,但是多了一些编译器生成的类型转换的字节码。

看一段简单的程序

@Test
public void t() {
    ArrayList<String> list = new ArrayList<>();
    list.add("one");
    String s = list.get(0);
}

生成的字节码

可以看到在20的位置出现了checkcast,就是检测类型是否匹配的,ArrayList.get()的方法签名,返回值返回的就是Object类型,并没有我们的String类型 。

所谓的泛型类型擦除,仅仅是对方法的code属性中的字节码进行擦除,但是类的原数据中还是保留了泛型信息。

根据上面的说明我们就可以知道,为什么不能在toArray上使用泛型了,因为泛型都是加强制类型转化,但是Object[]类型的数组是不能转换成Stringp[]类型的,所以toArray()方法只能返回object数组

2.为什么toArray(T[] t)可以利用泛型

因为这种情况下是可以进行强制类型转换的,因为你传递过去的数组是什么类型,返回的就是什么类型的数组,虽然方法的签名上还是返回的Object[]数组,但是他的实际类型是你传递过去的数组类型,所以说是可以进行强制类型转换的。

就像下面这样,编译成字节码其实还是加上的类型转换,但是这种类型转换是可以成功的

String [] arr = tokens.toArray(new String[0]);

源码部分

public <T> T[] toArray(T[] a) {
    if (a.length < size)
        // 利用了Arrays.copyof()方法,并且获取了我们的a.class
        return (T[]) Arrays.copyOf(elementData, size, a.getClass());
    System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;
}
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
    T[] copy = ((Object)newType == (Object)Object[].class)
        ? (T[]) new Object[newLength]
        // 利用Array.newInstance创建了一个数组,数组的类型就是我们传入的数组类型
        : (T[]) Array.newInstance(newType.getComponentType(), newLength);
    System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
    return copy;
}

相信大家应该都明白了

最后再说一点,toArray()方法返回的数组,都不是集合内部真实的存储数据的数组,而是一个copy,所以我们可以随意的修改返回的数组

发布了162 篇原创文章 · 获赞 44 · 访问量 8839

猜你喜欢

转载自blog.csdn.net/P19777/article/details/103653503