Java数组类型转换

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

public static void main(String[] args) {
        List<Integer> 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<Integer> 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)
}

看看运行效果



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

现在在设计一个小实验,我们修改类型转化之后的数组:
public static void main(String[] args) {
        List<Integer> 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)
}
运行结果



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

现在想想为何list.toArray(T[])为何能运行成功?
看下ArrayList.java源码:
    @SuppressWarnings("unchecked")
    public <T> 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运行看下效果:

换了角度理解我们知道,list.toArray(T[])参数带有泛型,就算我们自己可以轻易实现列表向数组转换。我们在看看list.toArray()在Debug模式下运行效果:



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

数组对象继续思考:
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));
    }
运行结果为:


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值