Java列表 T []指定者(T [] a)实施(Java List T[] toAr

2019-07-21 16:34发布

我只是看在列表接口中定义的方法: <T> T[] toArray(T[] a) ,并且我有一个问题。 为什么通用? 由于这一事实,方法是不完整的类型安全。 下面的代码片段编译,但会导致ArrayStoreException

List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);

String[] stringArray = list.toArray(new String[]{});

在我看来,如果指定者是不是通用的,把列表类型的参数,它会更好。

我已经写了玩具的例子,它是确定不用其他通用:

package test;

import java.util.Arrays;

public class TestGenerics<E> {
    private Object[] elementData = new Object[10];
private int size = 0;

    public void add(E e) {
    elementData[size++] = e;
}

@SuppressWarnings("unchecked")
    //I took this code from ArrayList but it is not generic
public E[] toArray(E[] a) {
    if (a.length < size)
        // Make a new array of a's runtime type, but my contents:
        return (E[]) Arrays.copyOf(elementData, size, a.getClass());
    System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;
}

    public static void main(String[] args) {

    TestGenerics<Integer> list = new TestGenerics<Integer>();
    list.add(1);
    list.add(2);
    list.add(3);
    //You don't have to do any casting
    Integer[] n = new Integer[10];
    n = list.toArray(n);
}
}

有为什么它宣布这样的原因吗?

Answer 1:

从的javadoc :

像toArray()方法,该方法充当基于收集基于阵列和API之间的桥梁。 此外,该方法允许在运行时类型的输出阵列的精确控制,并且可以在某些情况下,可以用来节省分配开销。

这意味着程序员是超过它应该是什么类型的数组的控制。

例如,对于您ArrayList<Integer> ,而不是一个Integer[]数组,你可能需要一个Number[]Object[]数组。

此外,该方法还检查传入的阵列。如果在具有所有元素的足够的空间的阵列通过,所述toArray方法重新使用该阵列。 这意味着:

Integer[] myArray = new Integer[myList.size()];
myList.toArray(myArray);

要么

Integer[] myArray = myList.toArray(new Integer[myList.size()]);

具有比相同的效果

Integer[] myArray = myList.toArray(new Integer[0]);

注意,在旧版本的Java的使用后一种操作反射检查数组类型,然后动态构建正确类型的阵列。 通过使在第一位置正确大小的数组,反射并没有被用于分配的内部的新的数组toArray方法。 这已不再的情况下,并且两个版本可以互换使用。



Answer 2:

它宣称一般这样就可以编写代码如

Integer[] intArray = list.toArray(new Integer[0]);

没有铸造阵列回来。

它宣布与以下注释:

@SuppressWarnings("unchecked")

换句话说,Java正在相信在同类型的数组参数来传递,所以不会出现你的错误。



Answer 3:

为什么该方法具有此签名的原因是因为toArray API早泛型:方法

 public Object[] toArray(Object[] a)

已经出台,早在Java 1.2的。

的相应的通用替换ObjectT已被引入作为100%向后兼容选项:

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

更改签名通用让呼叫者避免投:Java 5之前,呼叫者需要做的是:

String[] arr = (String[])stringList.toArray(new String[stringList.size()]);

现在,他们可以不用铸造相同的呼叫:

String[] arr = stringList.toArray(new String[stringList.size()]);

编辑:

对于一个更“现代”的签名toArray的方法是一对重载:

public <T> T[] toArray(Class<T> elementType)
public <T> T[] toArray(Class<T> elementType, int count)

这将提供一个更具表现力,同样多才多艺,替代目前的方法签名。 有一个高效实现这一点,也与Array.newInstance(Class<T>,int)方法在适当位置。 以这种方式更改签名不会向后兼容,虽然。



Answer 4:

类型安全的-它不会导致一个ClassCastException 。 这一般是什么类型安全的手段。

ArrayStoreException是不同的。 如果包括ArrayStoreException在“不是类型安全”,然后在Java中所有的数组不是类型安全的。

您发布的代码也产生ArrayStoreException 。 试一试:

TestGenerics<Object> list = new TestGenerics<Object>();
list.add(1);
String[] n = new String[10];
list.toArray(n); // ArrayStoreException

事实上,这是根本不可能允许用户在他们想要得到的类型的数组传递,并在同一时间没有ArrayStoreException 。 因为接受某些类型的阵列的任何方法签名也允许亚型的阵列。

那么既然是无法避免ArrayStoreException为什么不让它尽可能地通用? 这样用户可以使用一些不相关类型的数组,如果它们在某种程度上知道,所有的元素将是该类型的实例?



Answer 5:

我认为dasblinkenlight可能是正确的,这事做泛型化与现有的方法,并完全兼容是实现一种微妙的东西。

beny23的点也很不错-该方法应该接受的超类型E[] 有人可能会尝试

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

但Java不允许super上一个类型变量,由于缺乏使用情况:)

(编辑:不,这不是一个很好的用例super ,见https://stackoverflow.com/a/2800425/2158288 )



Answer 6:

之所以这种方法,因为它主要是历史性的。

有通用类和数组类型之间的区别:通用类的类型参数是在运行时被擦除,数组的元素的类型是没有的。 因此,在运行时,JVM中看到没有任何区别List<Integer>List<String> ,但它确实看到之间的差异Integer[]String[] 这样做的原因不同的是,阵列就一直存在,从Java 1.0起,而其中仅在Java中加入1.5(在向后兼容的方式)泛型。

集合API是在Java 1.2的补充,引入泛型之前。 当时List界面已经包含的方法

Object[] toArray(Object[] a);

(见1.2的Javadoc这个副本 )。 这是为了建立与用户指定的运行时类型的数组的唯一方式:参数a充当类型令牌,即,它确定所返回的数组的运行时类型(注意,如果A是的一个子类BA[]被认为是一个亚型B[]尽管List<A> 不是的子类型List<B>

当泛型Java 1.5中引入,许多现有的方法进行处理通用和toArray方法成为

<T> T[] toArray(T[] a);

其中,类型擦除后,具有相同签名的原始的非通用的方法。



文章来源: Java List T[] toArray(T[] a) implementation