ArrayList.toArray()中的Java泛型

ArrayList.toArray()中的Java泛型,java,arrays,generics,Java,Arrays,Generics,假设您有一个定义如下的arraylist: ArrayList<String> someData = new ArrayList<>(); 编译器清楚地知道它将得到一个字符串。耶,仿制药!但是,这将失败: String[] arrayOfData = someData.toArray(); toArray()将始终返回对象数组,而不是定义的泛型数组。为什么get(x)方法知道它返回什么,但是toArray()默认为对象?一般信息是在运行时提供的。JVM不知道您的列表是

假设您有一个定义如下的arraylist:

ArrayList<String> someData = new ArrayList<>();
编译器清楚地知道它将得到一个字符串。耶,仿制药!但是,这将失败:

String[] arrayOfData = someData.toArray();
toArray()
将始终返回对象数组,而不是定义的泛型数组。为什么
get(x)
方法知道它返回什么,但是
toArray()
默认为对象?

一般信息是在运行时提供的。JVM不知道您的列表是
list
还是
list
(在运行时
list
中的
T
被解析为
Object
),因此唯一可能的数组类型是
Object[]

   int[] ints = new int[3];
   String[] strings = (String[]) ints;//java: incompatible types: int[] cannot be converted to java.lang.String[]

   public <T> T[] a() {
      Object[] objects = new Object[3];
      return (T[])objects;
   }
   //ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
   Integer[] a = new LearnArray().<Integer>a();
您可以使用
toArray(T[]array)
但是-在这种情况下,JVM可以使用给定数组的类,您可以在
ArrayList
实现中看到它:

public <T> 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());
public T[]toArray(T[]a){
如果(a.长度<尺寸)
//创建a的运行时类型的新数组,但我的内容:
return(T[])array.copyOf(elementData,size,a.getClass());
如果您查看,您会注意到第二种形式的
toArray
T[]toArray(T[]a)

事实上,Javadoc甚至给出了一个示例,说明了如何准确地执行您想要执行的操作:


String[]y=x.toArray(新字符串[0]);

数组的类型与数组的类型不同。它是一种StringArray类而不是String类

假设有可能,一个通用方法
toArray()
如下所示

private <T> T[] toArray() {
    T[] result = new T[length];
    //populate
    return result;
}
private T[]toArray(){
T[]结果=新的T[长度];
//填充
返回结果;
}
现在在编译过程中,类型T被删除。如何替换部件
new T[length]
?通用类型信息不可用


如果您查看(例如)
ArrayList
的源代码,您会看到相同的情况。
toArray(T[]a)
方法要么填充给定数组(如果大小匹配),要么使用参数类型(即泛型类型T的数组类型)创建一个新数组。

如果您查看
toArray(T[]a)
类,类似于:

public <T> 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;
}
public T[]toArray(T[]a){
如果(a.长度<尺寸)
//创建a的运行时类型的新数组,但我的内容:
return(T[])array.copyOf(elementData,size,a.getClass());
System.arraycopy(elementData,0,a,0,size);
如果(a.长度>尺寸)
a[size]=null;
返回a;
}

此方法的问题是需要传递同一泛型类型的数组。现在考虑如果此方法不采取任何参数,则实现将类似于:

public <T> T[] toArray() {
    T[] t = new T[size]; // compilation error
    return Arrays.copyOf(elementData, size, t.getClass());
}
public T[]toArray(){
T[]T=newt[size];//编译错误
返回Arrays.copyOf(elementData,size,t.getClass());
}
但是这里的问题是,不能在Java中创建泛型数组,因为编译器不知道
T
代表什么。换句话说,在Java中不允许创建不可重写类型的数组

数组存储异常的另一个重要引用():

如果数组的组件类型不可重新定义(§4.7),则Java虚拟机无法执行 这就是为什么数组创建表达式 禁止使用不可恢复元素类型(§15.10.1)

这就是Java提供重载版本的原因

我将重写toArray()方法,告诉它将返回 E

因此,您应该使用
toArray(T[]a)
,而不是重写
toArray()

来自Javadoc的内容可能也会让您感兴趣

我可以,有时也会使用迭代器而不是数组,但这对我来说总是很奇怪。为什么get(x)方法知道它返回什么,但toArray()默认为对象?这就像是设计它的一半,他们认为这里不需要它

由于这个问题的目的似乎不仅仅是为了绕开泛型使用
toArray()
,而是为了理解
ArrayList
类中方法的设计,我想补充:

ArrayList
是一个泛型类,声明如下

public class ArrayList<E> extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable

Java编译器在编译时将
E
替换为
Object

需要注意的是,Java中的数组在运行时知道它们的组件类型。
String[]
Integer[]
在运行时是不同的类,您可以在运行时要求数组提供它们的组件类型。因此,在运行时需要组件类型(通过在编译时使用
新字符串[…]硬编码可重新定义的组件类型
,或者使用
数组.newInstance()
并传递类对象)来创建数组

   transient Object[] elementData;
另一方面,泛型中的类型参数在运行时不存在。
ArrayList
ArrayList
在运行时完全没有区别。它们都只是
ArrayList


这就是为什么不能只获取
列表
而获取
字符串[]的根本原因
如果不以某种方式单独传递组件类型,您必须从没有组件类型信息的东西中获取组件类型信息。显然,这是不可能的。

您首先要了解的是
ArrayList
所拥有的只是一个
对象的数组

   transient Object[] elementData;
当谈到
T[]
失败的原因时,这是因为没有
Class
就无法获得泛型类型的数组,这是因为java的类型擦除(and)。堆上的
array[]
动态地知道它的类型,并且不能将
int[]
强制转换为
String[]
   int[] ints = new int[3];
   String[] strings = (String[]) ints;//java: incompatible types: int[] cannot be converted to java.lang.String[]

   public <T> T[] a() {
      Object[] objects = new Object[3];
      return (T[])objects;
   }
   //ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
   Integer[] a = new LearnArray().<Integer>a();
  return (E) elementData[index];
public static <T> T[] toArray(Class<T> type, ArrayList<T> arrList) {
    if ((arrList == null) || (arrList.size() == 0)) return null;
    Object arr = Array.newInstance(type, arrList.size());
    for (int i=0; i < arrList.size(); i++) Array.set(arr, i, arrList.get(i));
    return (T[])arr;
}