为什么ArrayList实现使用Object ?

在Java ArrayList实现基础上的一个对象数组。
谁能解释一下为什么ArrayList使用数组Object[]来代替E[]进行数据存储? 使用Object[]什么好处?

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

简单的方法不编译:

 public class Container { E[] arr = new E[3]; // ERROR: Cannot create a generic array of E } 

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

有其他方法,但它们提供了一组不同的权衡。 有关详细讨论,请参阅如何在Java中创建通用数组?

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

也就是说,实例变量的编译时类型可以声明为Object[]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使用的类型检查,就像通用容器一样。 缺点是,从逻辑上讲,强制转换是谎言 – 我们知道我们创建了一个运行时类型为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 ,实际上任何设计合理的容器类,都不会将实现细节(如内部数组)暴露给外部。 除其他外,它会破坏抽象。 因此,只要这个类的作者知道没有暴露过这个数组,这样做是没有问题的(除了让下一个看到代码并且不知道它的人感到困惑),并且可以自由地使用它这种方式带来的类型检查增加的优点。

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

从维护的角度来看,使用类型参数而不是Object将导致更容易阅读代码(因为它会限制强制转换)。 但是由于ArrayList的API从未暴露过那个“原始”的Object数组,我认为它对我们仅仅是Java开发人员没有任何影响:)