为什么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[]
的实例,除非E
是Object
。
但是,这样做没有直接问题,因为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开发人员没有任何影响:)