12

方法(让我们在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 类本身已经是泛型的<E>,所以我们可以使用它来代替新的泛型类型<T>吗?

4

2 回答 2

12

我认为约翰 B 的回答很好地涵盖了这个想法 - 我只想详细说明一下。

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

public E[] toArray(E[] a)

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

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

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

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

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

好吧,这个语法:

 <T super E>

语言不支持。但这很容易分散您的注意力,因为这种类型检查无论如何都不起作用。问题在于,与参数化类型不同,数组是协变的。一个Integer[]是一个Number[]是一个Object[]。因此,考虑到我们假设的签名<T super E>,如果我调用toArrayanArrayList<Number>并传入 an Integer[],它仍然会编译 - 并且可能在运行时失败,具体取决于列表中的内容。

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

于 2012-09-11T04:59:19.837 回答
6

的要点<T>是,如果所需的数组是 的基类E。例如 if EisHashMap但所需的数组 was Map[]。如果toArray被锁定到E这将是不可能的。

由于类型擦除,在泛型集合/类型中不需要这种类型的东西。但是数组没有类型擦除,因此数组的类型可能非常重要。

于 2012-09-10T16:41:46.427 回答