开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第5天,点击查看活动详情
泛型与数组,Java引入泛型后,规约是不能创建泛型数组的。比如可能想创建一个 Pair 的泛型数组。
Pair<Object, Integer>[] options = new Pair<Object, Integer>[]{
new Pair("大山",7), new Pair("树林", 2), new Pair("湖泊", 1)
};
会提示编译错误,不能创建泛型数组。要先来进一步熟悉一下数组。
类型参数之间有继承关系的容器是毫无关系的,如一个DynamicArray<Integer>
对象不能赋值给一个DynamicArray<Number>
变量。但数组是可以的,看代码:
Integer[] ints = new Integer[10]; // 创建数组
Number[] numbers = ints;
Object[] objs = ints;
下面两种赋值都是允许的。数组为啥可以?数组元素类型是知晓的,Object和Number都是Integer的父类型,所以操作是允许的。
但若使用不当,可能会引起运行时异常:
Integer[] ints = new Integer[10];
Object[] objs = ints;
objs[0] = "hello";
编译没有问题,运行时会抛出ArrayStoreException
,因为实际的类型是Integer,写入String会抛出异常。
若需要能够存放泛型对象的容器,该如何处理呢?使用原始类型的数组,如:
Pair[] options = new Pair[]{
new Pair<String, Integer>("大山",7),
new Pair<String, Integer>("树林", 2),
new Pair<String, Integer>("湖泊", 1)};
还有一种更优选择,使用泛型容器:
DynamicArray<Pair<String, Integer>> options = new DynamicArray<>();
options.add(new Pair<String, Integer>("大山",7));
options.add(new Pair<String, Integer>("树林",2));
options.add(new Pair<String, Integer>("湖泊",1));
DynamicArray内部的数组为Object类型,一些操作插入了强制类型转换,外部接口是类型安全的,对数组的访问都是内部代码,可以避免误用和类型异常。
有时,希望转换泛型容器为一个数组,如对于DynamicArray,可能希望它有这么一个方法:
public E[] toArray()
可以这么用:
DynamicArray<Integer> ints = new DynamicArray<Integer>();
ints.add(100);
ints.add(34);
Integer[] arr = ints.toArray();
先使用动态容器收集一些数据,然后转换为一个固定数组,是一个常见的合理需求,但如何来实现这个toArray方法呢:
一种想法:
public E[] toArray(){
Object[] copy = new Object[size];
System.arraycopy(elementData, 0, copy, 0, size);
return (E[])copy;
}
或者使用Arrays方法:
public E[] toArray(){
return (E[])Arrays.copyOf(elementData, size);
}
结果是一样的,没有编译错误,但运行时会抛出ClassCastException
异常,原因是 Object类型的数组不能转换为Integer类型数组。
这里可利用Java中的运行时类型和反射机制,在运行时知道要转换成的数组类型,类型可以作为参数传递给toArray方法:
public E[] toArray(Class<E> type){
Object copy = Array.newInstance(type, size);
System.arraycopy(elementData, 0, copy, 0, size);
return (E[])copy;
}
Class<E>
表示要转换成的数组类型信息,有了类型信息,Array类的newInstance方法就可以创建出真正类型的数组对象。调用toArray方法时,传递需要的类型:
Integer[] arr = ints.toArray(Integer.class);
总结泛型与数组关系:
- Java不支持创建泛型数组。
- 若要存放泛型对象,可使用原始类型的数组,或者用泛型容器。
- 泛型容器内部使用Object数组,若要转换泛型容器为对应类型的数组,需要使用反射。