为什么Collection.toArray(T [])不采用E []

pet*_*ter 11 java generics arraylist

toArray方法(可以接在执行java.util.ArrayList)如下:

class ArrayList<E> ....{
    public <T> T[] toArray(T[] a){
        if(a.length < size)
            return (T[]) Arrays.copyof(elementData, size, a.getClass());
        System.arraycopy(elementData, 0, a, 0, size);
        if(a.length > size)
            a[size] = null;
        return a;
    }    
}
Run Code Online (Sandbox Code Playgroud)

我想知道我们可以使用<E>而不是<T>在这种情况下吗?喜欢

public E[] toArray(E[] a){
      if(a.length < size)
             return (E[]) Arrays.copyof(elementData, size, a.getClass());
      System.arraycopy(elementData, 0, a, 0, size);
      if(a.length > size)
            a[size] = null;
      return a;
}    
Run Code Online (Sandbox Code Playgroud)

由于ArrayList类iteself已经是通用的<E>,所以我们可以使用它而不是新的泛型类型<T>吗?

Pau*_*ora 11

我认为John B的答案很好地涵盖了这个想法 - 我只想详细说明一下.

首先,让我们看一下您在问题中提出的方法签名:

public E[] toArray(E[] a)
Run Code Online (Sandbox Code Playgroud)

正如约翰所解释的那样,这种签名不太灵活.如果我要转储ArrayList<Integer>成a Number[],这个方法不让我.Collections API希望允许这种灵活性.

不幸的是,现有的方法不允许对数组类型进行编译时检查,这是您似乎遇到的问题.toArray由此声明的文档Collection解释说,ArrayStoreException如果指定数组的运行时类型不是此集合中每个元素的运行时类型的超类型,则可能抛出一个文档.

根据该描述,似乎以下签名是理想的:

public <T super E> T[] toArray(T[] a)
Run Code Online (Sandbox Code Playgroud)

乍一看,这似乎允许传入和填充任何合法类型的数组 - 但是会在编译时而不是运行时提供类型检查.那么为什么这个签名没有被声明呢?

好吧,这个语法:

 <T super E>
Run Code Online (Sandbox Code Playgroud)

不被支持的语言.但是这很容易让你分心这种类型检查无论如何都不会起作用的事实.问题是,与参数化类型不同,数组是协变的.一个Integer[]是一个Number[]是一个Object[].因此,与给定我们假设的签名<T super E>,如果我叫toArrayArrayList<Number>,并在传递Integer[],它仍然编译-并在运行时可能失败,这取决于是在列表中.

所以底线是,传入的数组的组件类型的下限不会有任何好处.


Joh*_*n B 5

的点<T>是如果需要的话,所述阵列是基类的E.例如,如果E是,HashMap但所需的数组是Map[].如果toArray被锁定到E这将是不可能的.

由于类型擦除,在通用集合/类型中不需要这种类型的东西.但是阵列没有类型擦除,因此阵列的类型非常重要.