Java generics in ArrayList.toArray()

2022-08-31 15:36:52

假设您有一个数组列表,定义如下:

ArrayList<String> someData = new ArrayList<>();

稍后在你的代码中,由于泛型,你可以这样说:

String someLine = someData.get(0);

编译器完全知道它将获得一个字符串。耶泛型!但是,这将失败:

String[] arrayOfData = someData.toArray();

toArray()将始终返回对象数组,而不是已定义的泛型数组。为什么该方法知道它返回的内容,但默认为对象?get(x)toArray()


答案 1

如果你看看ArrayList<E>类的实现,它就像: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> T[] toArray() {
    T[] t = new T[size]; // compilation error
    return Arrays.copyOf(elementData, size, t.getClass());
}

但这里的问题是,你不能在Java中创建泛型数组,因为编译器不知道确切的表示是什么。换句话说,Java中不允许创建不可重用类型的数组JLS §4.7)。T

数组存储异常JLS §10.5)中的另一个重要引用:

如果数组的组件类型不可重用 (§4.7),则 Java 虚拟机无法执行上一段中描述的存储检查。这就是禁止使用不可 reifiable 元素类型的数组创建表达式的原因 (§15.10.1)。

这就是为什么Java为Array(T[] a)提供了重载版本。

我将重写 toArray() 方法,告诉它将返回一个 E 数组。

因此,您应该使用 ..您不应该覆盖 。toArray()toArray(T[] a)

您无法从 Java 文档创建类型参数的实例对您来说也可能很有趣。


答案 2

一般信息在运行时被擦除。JVM 不知道您的列表是 还是 (在运行时解析为 ),因此唯一可能的数组类型是 。List<String>List<Integer>TList<T>ObjectObject[]

你可以使用虽然 - 在这种情况下,JVM可以使用给定数组的类,你可以在实现中看到它:toArray(T[] array)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());