首先准备几点:

1.定义泛型方法需要在返回值前加上泛型参数。就是public T method(T a){...}这样

2.类的参数是E,即Arraylist,与泛型方法public T[] toArray(T[] a)中的参数T不同,注意!

3.父类对象能够显式强制转换为子类对象的前提:该对象本质上是子类(或子类的子类)的对象。

Object o=new String("sss");//o是一个子类对象String的引用
String s=(String)o;//不出错
o=new Object();//o不是子类对象的引用
s=(String)o;//出错


懂得都懂,大意就是子类对象变成父类对象后,父类对象才能使用强制转换变回子类对象

4.数组也是对象,由jvm提供定义和初始化。但Object[]类型与String[]类型之间无继承关系(例子)。

但是他们有协变(java少有的特例支持),使得数组对象也能像父类子类一样转换。一样的举例:

Object[] o=new String[]{"sss"};
String[] s=(String[])o;//不出错
o=new Object[]{};
s=(String[])o;//出错

上面看完,讲讲为什么public T[] toArray(T[] a)写成这样。(以Arraylist list举例)

由于泛型被擦除,实际Arraylist的储存结构就是一个Object[] 数组对象。因此另一个重载方法public Object[] toArray()直接就把该Object[] 返回。

问:为什么不直接public E[] toArray()这样返回呢?

首先看大佬博客分析,这里E被擦除后就等同是Object,分析class文件中可看出方法实际返回仍是Object[]而并非String[],最后是由编译器偷偷分析list对象的声明,再马上追加强制转换(String[])到结果上(这个过程用户感觉不到)。

所以这样结果就是方法返回Object[]对象,编译器尝试把Object[]强制转换为String[]对象,根据上面提醒过的注意事项,这样的转换是不能进行的。(同理,你肯定试过String[] strs=(String[]) Arraylist.toArray();然后发现出错,其实就是和上面所说的编译器做一样的事情)。所以只要不是Arraylist,就肯定会有类型转换错误的提示。

问:所以加个参数T[] a就能解决了吗?

是的,主要思路:Object[]中数组元素实际上都是String对象(因为你用add()的时候都是add一个String进去),只要把元素逐个强制转换为String,再加入到一个String[] strs里面,然后返回(Object[])strs,最后编译器追加强制转换(String[])到结果上时,就不会出错(子类->父类->子类,没问题)。

这个a最主要目的就是通过a.getClass()获取类型信息,来让方法知道去把Object强制转换成String。其次是空间足够时直接用来放转换出来的String对象。源码:

public T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}

想知道a.getClass()可以怎样提供信息的,右转java反射:java反射