关于 c.toArray might (incorrectly) not return Object[]以及为什么一定要返回Object数组的原因

在学习ArrayList过程中,看到了源码出现这样的一段话:

    /**
     * Constructs an empty list with the specified initial capacity.
     *
     * @param  initialCapacity  the initial capacity of the list
     * @throws IllegalArgumentException if the specified initial capacity
     *         is negative
     */
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

    /**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * 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;
        }
    }

这是它的三个构造函数,核心在这个:


    /**
     * 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;
        }
    }

这里大概就是说传入参数为集合类,elementData 为一个Object数组,这里有几个问题:
首先是这里面的一句注释: c.toArray might (incorrectly) not return Object[] (see 6260652) 这里的意思是toArray可能会不正确地返回数组对象Object[];举个例子

@Test
public void toArrayTest(){
  
    List<String> StringList = new LinkedList<String>();
    StringList.add("123");
    Object[] objs = StringList.toArray();
    System.out.println(objs.getClass());
    objs[0] = new Object();

    objs= Arrays.asList("zhen", "xiang").toArray();
    System.out.println(objs.getClass());
    objs[0] = new Object();
    System.out.println(Arrays.asList("zhen", "xiang").toArray().getClass());
  
}

输出:
class [Ljava.lang.Object;
class [Ljava.lang.String;

java.lang.ArrayStoreException: java.lang.Object

	at com.example.demo.test1.toArrayTest(test1.java:79)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

可以看到,执行完ArrayList构造函数的elementData = c.toArray()之后,这个时候正如注释所说的,toArray可能会不正确地返回数组对象Object[],这里就返回了String[];这个其实是个官方bug,可以去到官方bug库(链接:https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6260652 )找得到相关的解释,并且这个是jdk1.5的bug;不过在jdk10已经修复了。我这个是jdk8,从这里可以知道的是,调用toArray() 时,实际上调用的是其具体实现类中的 toArray() 方法。如果这个时候你进行了向下转型(上面例子里的objs[0] = new Object(),这里objs是String数组,但是却进行了向下转型)是不安全的,会抛出ArrayStoreException异常。

这个有什么关系呢?有的,现在先不说,现在只要记得存在这样一个bug就行,接下来继续看构造函数,可以看到在进行完toArray操作之后,他会对elementData长度进行判断,如果不是0,那么会进行这样的判断:

if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);

重点来看一下这里,上面知道,elementData.getClass() != Object[].class是存在的,而elementData本身就是一个Object[],看起来似乎elementData = Arrays.copyOf(elementData, size, Object[].class);这个操作有点多余,因为就算不进行这样的操作,那么这里也不会出问题。实际上,会出问题的,这里不出问题,别的地方出问题而已,答案其实已经在上面的例子里面了:如果不进行调用Arrays.copyOf()方法,那么可能会出现抛出ArrayStoreException异常的情况。比如:

  ArrayList<Object> objectList = new ArrayList<Object>(Arrays.asList("zhen", "xiang"));
System.out.println(Arrays.asList("zhen", "xiang").toArray().getClass());
objectList.set(1,new Object());

如果这里不进行Arrays.copyOf操作,上面这个例子就会抛出ArrayStoreException异常

那么,这里Arrays.copyOf干嘛用的应该就清楚了:就是把数组强行转换成Object[]

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值