Java初探-12.Java中泛型和数组

39 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 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数组,若要转换泛型容器为对应类型的数组,需要使用反射。