Java笔记---c.toArray might (incorrectly) not return Object[] (see 6260652)官方Bug

一、前言

在分析ArrayList源码是,看到toArray()有这么一句:c.toArray might (incorrectly) not return Object[] (see 6260652)。网上百度一下,原来,这是一个官方bug。经过本人分析,说的就是public Object[] toArray() 返回的类型不一定就是 Object[],其类型取决于其返回的实际类型.

二、bug 分析

ArrayList 中 toArray() 的源码如下:

public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
}

该方法为什么返回的不一定就是 Object[] 呢?原因很简单,因为由于继承的原因,我们父类实例的具体类型,实际上是取决于在 new 时,我们所使用的子类类型。

2.1 以代码说明

我们用代码进行说明这个问题。

创建类

首先创建2个类。

public static class Father {

}

public static class Son extends Father {

}

测试案例1

我们先进行第一次测试,测试代码如下:

/**
     * 测试 java.lang.ArrayStoreException 
     * - 父类(抽象类、接口)对象的实际类型,取决于其实例化时子类的类型
     */
    @org.junit.Test
    public void test1 () {
        Son[] sons = new Son[]{new Son(), new Son()};
        System.out.println(sons.getClass());            // class [Lcom.johnnie.test.Test$Son;

        Father[] fathers = sons;
        System.out.println(fathers.getClass());     // class [Lcom.johnnie.test.Test$Son;

//      fathers[0] = new Father();                          // java.lang.ArrayStoreException
    }

对于 fathers[0] = new Father(); 这句话,为什么会报错?报错原因是因为 fathers 实际类型是 Son[],由于数组中元素类型都是son类型的,因而会出现向下转型异常。也就是说假如我们有 1 个Object[]数组,并不代表着我们可以将Object对象存进去,这取决于数组中元素实际的类型

测试案例2

我们现在进行第二次测试,这次测试就会真正抛出这个官方错误,并且让我们知道为什么会报错。代码如下:

① 新建一个 MyList,该类继承 ArrayList

    @SuppressWarnings("serial")
    public class MyList extends ArrayList {

        // toArray() 的同名方法
        public String[] toArray() {
            return new String[]{"1", "2", "3"};
        }

    }

② 测试代码

/**
     * 测试:c.toArray might (incorrectly) not return Object[] (see 6260652) 这个官方 bug
     */
    @org.junit.Test
    public void test3() {
        List ss = new LinkedList();             // LinkedList toArray() 返回的本身就是 Object[]
        ss.add("123");
        Object[] objs = ss.toArray();
        objs[0] = new Object();

        // 此处说明了:c.toArray might (incorrectly) not return Object[] (see 6260652)
        ss = new MyList();
        objs = ss.toArray();
        System.out.println(objs.getClass());        // class [Ljava.lang.String;
        objs[0] = new Object();                         // java.lang.ArrayStoreException: java.lang.Object
    }

通过控制打印,我们知道,当我们调用 List的 toArray() 时,我们实际上调用的是其具体实现类 MyList 中重写的 String[] toArray() 方法。因此,返回数组并不是 Object[] 而是 String[],而向下转型是不安全的,因此会抛出异常。

通过分析这个官方bug,我们就记住了:
1. 抽象类(接口)其具体类型,取决于实例化时所采用的导出类的类型。
2. 子类实现和父类同名的方法,仅仅返回值不一致时,默认调用的是子类的那个实现方法。如 MyList 中的 String[] toArray()

前往 bascker/javaworld 获取更多 Java 知识

你可能感兴趣的:(Java)