12

在 JavaArrayList<E>实现中基于对象数组。
谁能解释我为什么ArrayList<E>使用数组Object[]而不是数据存储的实现E[]?使用有什么好处Object[]

4

3 回答 3

8

所以首先要意识到数组对象的实际运行时类型必须是Object[]. 这是因为数组在运行时知道其组件类型(不同的数组类型在运行时实际上是不同的类型),因此您需要在创建数组时指定组件类型,而ArrayList对象在运行时不知道其类型参数。

也就是说,实例变量的编译时类型可以声明为Object[]or E[],具有不同的优点和缺点:

如果它被声明为Object[]

private Object[] arr;
// to create it:
arr = new Object[3];
// to get an element:
E get(int i) { return (E)arr[i]; }

这样做的缺点是每次从其中取出东西时都必须将其转换E为,这意味着您基本上将其用作预泛型容器。

如果它被声明为E[]

private E[] arr;
// to create it:
arr = (E[])new Object[3];
// to get an element:
E get(int i) { return arr[i]; }

这样做的好处是,当您从中获取内容时,您不再需要进行强制转换——它提供了对 . 的使用的类型检查arr,例如通用容器。缺点是,从逻辑上讲,强制转换是 lie——我们知道我们创建了一个运行时类型为 的对象Object[],因此它不是 的实例E[],除非EObject

但是,这样做不会立即出现问题,因为E它会被擦除到Object类的实例方法内部。可能发生问题的唯一方法是,如果对象以某种方式暴露于类的外部(例如,在方法中返回,放入公共字段等),其类型使用其类型E[](事实并非如此):

// This would be bad. It would cause a class cast exception at the call site
E[] getArray() { return arr; }

但是ArrayList,实际上任何设计合理的容器类,都不会向外部公开诸如其内部数组之类的实现细节。除其他外,它会破坏抽象。所以只要这个类的作者知道永远不会暴露这个数组,这样做就没有问题(可能会让下一个看到代码但不知道它的人感到困惑),并且可以自由使用这种方式带来的增加类型检查的优势。

于 2012-12-08T22:37:58.507 回答
8

在 Java 中,创建泛型类型的数组并不简单。

简单的方法不编译:

public class Container<E> {

    E[] arr = new E[3]; // ERROR: Cannot create a generic array of E

}

替换EObject, 一切都很好(以在容器实现的其他地方增加复杂性为代价)。

有替代方法,但它们提出了一组不同的权衡。有关广泛的讨论,请参阅如何在 Java 中创建泛型数组?

于 2012-12-08T10:25:09.287 回答
4

考虑到类型擦除(即在编译类型时删除了示例中的泛型类型参数这一事实E),我怀疑在这两种情况下生成的字节码会相似。

从维护的角度来看,使用类型参数而不是 Object 会更容易阅读代码(因为它会限制强制转换)。但是由于 APIArrayList从不公开那个“原始”Object数组,我想它对我们这些单纯的 Java 开发人员没有任何影响 :)

于 2012-12-08T10:31:50.483 回答