Java数组类型转换

在做项目的过程中,遇到一个很奇怪的问题。为了说明清楚,先举个栗子:

public static void main(String[] args) {
        List list = new ArrayList<>();
        list.add(2);
        Integer[] a1 = list.toArray(new Integer[list.size()]);//(1)
        Integer[] a2 = (Integer[]) list.toArray();//(2)
}

咋一看,会觉得上面的代码没有问题。实际情况是代码(1)处是没有问题的,而(2)处会发生ClassCastException;list明明泛型中指定了里面参数的类型,为何还是会发生类型转换的错误?我们知道Java泛型只是编译器有效,运行期泛型是要被擦除的。

其实这个问题解释起来也不复杂,我们知道Java一切皆对象,那么数组也必然为数组对象了。我们加些打印语句:

public static void main(String[] args) {
        List list = new ArrayList<>();
        list.add(2);
        Integer[] a1 = new Integer[1];//(1)
        Integer[] a2 = new Integer[1];
        System.out.println("a1对象类型: " + a1.getClass());
        System.out.println("a2对象类型: " + a2.getClass());
        System.out.println("list.toArray(new Integer[list.size()])对象类型: "
                             + list.toArray(new Integer[list.size()]).getClass());
        System.out.println("list.toArray()对象类型: " + list.toArray().getClass());
        a1 = list.toArray(new Integer[list.size()]);
        a2 = (Integer[]) list.toArray();//(2)
}

看看运行效果

Java数组类型转换_第1张图片

看见没有a2与list.toArray()根本不是一种类型。运行时list.toArray()对象也不是Integer[]类型的实例。为了验证该说话继续添加代码:
System.out.println(list.toArray() instanceof  Integer[]);
输出结果为false.

现在在设计一个小实验,我们修改类型转化之后的数组:
public static void main(String[] args) {
        List list = new ArrayList<>();
        list.add(1);
        Object[] a1 = list.toArray();//(1)
        Object[] a2 = list.toArray(new Integer[list.size()]);//(2)
        System.out.println("a1对象类型" + a1.getClass());
        System.out.println("a2对象类型" + a2.getClass());
        //修改元素
        a1[0] = new BigDecimal(5);//(3)
        a2[0] = new BigDecimal(5);//(4)
}
运行结果

Java数组类型转换_第2张图片

我们知道BigDecimal和Integer类已经没有半毛钱的关系了,(3)运行成功,(4)抛出异常。说明list.toArray()之后,该数组可以存放任何对象均是合法的,所以将这样的数组对象转型为Integer[]合法的话那Java类型转化就乱套了。

现在想想为何list.toArray(T[])为何能运行成功?
看下ArrayList.java源码:
    @SuppressWarnings("unchecked")
    public  T[] toArray(T[] a) {
        if (a.length < size)
            // Make a new array of a's runtime type, but my contents:
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
        System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    }
起作用的为 System.arraycopy(elementData, 0, a, 0, size),这里直接返回的数组对象a是带有泛型的;这其实为native方法,和 Java无关了!
Debug运行看下效果:
Java数组类型转换_第3张图片
换了角度理解我们知道,list.toArray(T[])参数带有泛型,就算我们自己可以轻易实现列表向数组转换。我们在看看list.toArray()在Debug模式下运行效果:

Java数组类型转换_第4张图片

虽然这里的泛型类型已经丢了,数组中存储的对象类型没有问题,但是返回的数组泛型已经丢了。

数组对象继续思考:
1.Integer[] 类型的数组对象是否也是Object[]的实例?
2. 声明Object[]类型的数组对象,运行期间一定为Object[]类型吗?若声明之后直接全部实例化为StringBuilder对象呢?
如:
    public static void main(String[] args) {
        Integer[] a1 = new Integer[1];
        System.out.println("a1 instanceof Integer[]:\t" + (a1 instanceof Integer[]));
        System.out.println("a1 instanceof Object[]:\t"+ (a1 instanceof Object[]));
        Object[] a2 = {new StringBuilder("abc"),new StringBuilder("bcd")};//只是实例化,并不返回类型
        //System.out.println({new StringBuilder("abc"),new StringBuilder("bcd")});//语法错误
        System.out.println(a2.getClass());
        Object[] a3 = a1;
        System.out.println(a3.getClass());
        Class c1 = Integer[].class;
        Class c2 = Object[].class;
        System.out.println("Integer[]是否为Object[]子类型:" + c2.isAssignableFrom(c1));
    }
运行结果为:
Java数组类型转换_第5张图片

从中我们可以得出下面的结论:
1.数组类型也存在子父类(或接口)之间关系
2.变量声明并不能决定其最终类型。
3.数组初始化{}本身不具备类型。

你可能感兴趣的:(java)