数组的强制类型转换
- 数组的强制类型转换
- 数组类型转换的问题为什么会出现在我脑海中?
数组的强制类型转换
最重要的是!!!最开始的时候声明的数组类型!!! 最重要的是!!!最开始的时候声明的数组类型!!! 最重要的是!!!最开始的时候声明的数组类型!!!
例如:
代码语言:javascript复制Object[] objects = new Object[2];
Object[0] = "hello";
Object[1] = "world";
Object[] objects1 = {"kkkk", 3434}
System.out.println(objects.getClass());
System.out.println(objects1.getClass());
System.out.println(objects[0].getClass());
System.out.println(objects[1].getClass());
System.out.println(objects1[0].getClass());
System.out.println(objects1[1].getClass());
class [Ljava.lang.Object;
class [Ljava.lang.Object;
class java.lang.String
class java.lang.String
class java.lang.String
class java.lang.Integer
根据实验,一开始就声明为String数组,可以在需要时自动转为Object数组,之后可以通过强制类型转换再转回String数组。
但是,如果一开始就声明为Object数组,那么,即便这个数组中存放的全部是String对象,也是不能转换为String数组的!!!
数组类型转换的问题为什么会出现在我脑海中?
先上代码:
代码语言:javascript复制public class Base {
public ArrayList<String> data;
public String[] getData() {
return (String[])data.toArray();
}
public void setData(String[] data) {
this.data = new ArrayList<String>(Arrays.asList(data));
}
}
遇到的问题如下:当调用setData设置好data之后,紧接着getData()将会抛出一个类型转换异常java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
提示不能将String数组强转为Object数组,这是为什么呢???明明我是String泛型的ArrayList呀,怎么一toArray()就成了Object数组了呢???而且这个Object数组为什么不能强转成String数组呢?我自己用String数组转换成Object数组之后,是可以转回String数组的呀,为什么这里就转不回去,而且报错了呢??????
带着问题的 我 和 源码 展开了“深入”交流 参照源代码的时候发现,首先,Arrays.asList(data)依然返回的是泛型,也就应该是String[]。因此,上述ArrayList的那个构造方法,传入参数时,应该是String[]。
这就神奇了,既然我参数传入的时候还是String数组,为什么data.toArray();就成了Object数组了呢???难道是data.toArray()
这个方法在搞鬼??
带着疑问,继续阅读源码,发现这个无参的toArray()方法转向了Arrays类的静态方法Arrays.copyOf(elementData, size);
,之后又转向了copyOf
的三参数重载方法:
class ArrayList {
... ...
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
}
代码语言:javascript复制class Arrays {
... ...
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
... ...
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
}
那么问题来了,这个作为参数传入的ArrayList的实例域elementData
实际上是什么类型的数组呢?这个3个参数的copyOf又干了什么呢???
首先,看看这个三参数copyOf()
函数干了什么:我们不妨假设elementData.getClass()依然是String数组。如果这样的话,应该会调用?:
表达式的第二个代码:
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
Array.newInstance()
返回的是Object引用类型,并最终导向native代码。虽然我不清楚它做了什么,但是有两点可以确定:
- 源代码既然在强转的时候没有报错,说明该方法的返回的实际对象一定是T子类的数组。而T类型是什么呢?因为假设传入的是String数组的Class对象,且这里String除了Object之外没有超类了,所以T必为Object类型;
- 很显然冒号左侧才是新建了一个Object数组,那冒号右面猜测应该是建立了一个泛型数组,但是暂时不确定。
- 在返回到二参数copyOf()方法中的时候,这里的T数组我们已经假设为String数组(因为我们假设elementData是String数组),在将
Array.newInstance()
产生的数组强转为T数组(String数组)时没有报错,说明,Array.newInstance()
产生的必然是String数组。
也就是说,在二参数copyOf()返回时,返回类型必然和elementData是同一类型,那我们又假设elementData是String数组,最后toArray()返回就不是String数组吗???怎么之后强转会出错呢?
那只能说明假设错了!!!!!
那说明elementData已经不是String类型了!那是什么时候变的呢?我们来到了它的构造方法:
代码语言:javascript复制class ArrayList {
......
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;
}
}
}
看到这个方法的第4、5行,已经当场气死~原来在构造方法的时候,就已经将不是Object数组的数组转成了Object数组,并存放在elementData中,而无参的toArray()方法并没有对其特殊处理,直接返回了一个Object数组(虽然里面元素仍然是String)。
所以,如果要返回泛型数组,该怎么办呢? 答案是:调用带参的toArray()方法!!