最近看到一段JDK中ArrayList类中的源码,其中一段注释吸引了我的眼球,为了弄清其含义,特意找了一些资料,写了一点测试代码。源代码如下:
/** * Constructs a list containing the elements of the specified * collection, in the order they are returned by the collection's * iterator. * * @param c the collection whose elements are to be placed into this list * @throws NullPointerException if the specified collection is null */ public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; } }为了弄清楚 // c.toArray might (incorrectly) not return Object[] (see 6260652) 这句注释的意义,特意查了一下官方的bug文档,Bug ID:JDK-6260652,地址如下
http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6260652
官方文档上有这样一段描述
A DESCRIPTION OF THE PROBLEM : The Collection documentation claims that collection.toArray() is "identical in function" to collection.toArray(new Object[0]); However, the implementation of Arrays.asList does not follow this: If created with an array of a subtype (e.g. String[]), its toArray() will return an array of the same type (because it use clone()) instead of an Object[]. If one later tries to store non-Strings (or whatever) in that array, an ArrayStoreException is thrown.
用我稀烂的英语翻译了一下就是:
该问题的描述Collection文档上是这样声称的
toArray(new Object[0])函数和toArray()是相同的。
然而,Arrays.asList的实现并不遵循这一点:如果用子类创建了一个数组,比方说String[]数组,它的toArray()方法将返回一个相同类型的数组(因为使用了clone()方法)而不是一个Object[]数组。
如果后来试图存储一个非String类型(或者其他类型)的数据到这个数组中,就会抛出ArrayStoreException 。
为此我特意翻看了JDK中Collection接口的API文档,确实有那么一句描述
通过API文档发现这两个方法并不是完全一致
Object[] toArray()
一个返回的是确定的Object[]数组,另一个是泛型,根据运行时的类型来确定。<T> T[] toArray(T[] a)
结合JDK的API文档和官方的bug文档,基本可以确定就返回类型导致抛出ArrayStoreException。
接下来写了2个测试的例子:
public static void main(String[] args) { String[] array = {"a","b"}; Object[] o = array; System.out.println(o.getClass()); o[0] = new Object(); }运行结果:
class [Ljava.lang.String;
Exception in thread "main" java.lang.ArrayStoreException: java.lang.Object
在第一个例子中将String[]类型的数组向上转型成为Object[]数组,而真正在运行时,打印出来该数组的实际类型依旧是String[]数组,之后向该数组中插入一个Object类型的元素时,抛出异常。
public static void main(String[] args) { List<String> list = Arrays.asList("a","b"); System.out.println(list.getClass()); Object[] o = list.toArray(); System.out.println(o.getClass()); o[0] = new Object(); }运行结果:
class java.util.Arrays$ArrayList
class [Ljava.lang.String;
Exception in thread "main" java.lang.ArrayStoreException: java.lang.Object
在第二个例子中声明了一个List<String>的集合,运行时得到的是Arrays的内部类ArrayList类型,通过toArray()方法后,实际得到的是String[]数组,也不是Object[]数组,之后插入元素的时候抛出异常。
正因为运行时可能会出现ArrayStoreException,ArrayList类中判断了数组类型不是Object类型时,新建了一个Object[]数组,已保证不会出现异常
// c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class);