您的位置:首页 > 编程语言 > Java开发

Java数组类型转换

2016-06-23 21:57 656 查看
在做项目的过程中,遇到一个很奇怪的问题。为了说明清楚,先举个栗子:

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.数组初始化{}本身不具备类型。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: