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

时间:2012-09-10 16:38:03

标签: 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;
    }    
}

我想知道在这种情况下我们可以使用<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;
}    

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

2 个答案:

答案 0 :(得分:11)

我认为John B's answer很好地涵盖了这个想法 - 我只想详细说明一下。

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

public E[] toArray(E[] a)

正如约翰所解释的那样,这种签名不够灵活。如果我想将ArrayList<Integer>转储到Number[],此方法不允许我这样做。 Collections API希望允许这种灵活性。

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

根据该描述,似乎,就像以下签名一样理想:

public <T super E> T[] toArray(T[] a)

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

嗯,这句语法:

 <T super E>

not supported by the language。但是这很容易让你分心这种类型检查无论如何都不会起作用的事实。问题是与参数化类型不同,arrays are covariantInteger[]Number[]Object[]。所以考虑到<T super E>的假设签名,如果我在toArray上调用ArrayList<Number>并传入Integer[],它仍会编译 - 并且可能在运行时失败,具体取决于什么在列表中。

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

答案 1 :(得分:5)

<T>的要点是,如果所需的数组是E的基类。例如,如果EHashMap,但所需的数组为Map[]。如果toArray被锁定为E,那将无法实现。

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