如何创建泛型数组

作者:wenyinfeng

转载时,请注明原文出处,谢谢!

 

先复习下数组的一些特性。

 

数组支持向上转型,类似下面的转换是合法的自动的,不需要强制(Apple Fruit 的一个子类)。

Fruit[] fruit = new Apple[10];

 

但如果在不同类型的数组之间进行强制转换(不是向上转型),那么就会出现ClassCastException 异常。注意,由于擦除的实现  array = (T[])new Object[sz]; 并不会抛出异常。

++++++++++++++++++++++++++++++++++++++++++++++++++++++

//: generics/GenericArray.java

 

package generics;

public class GenericArray<T> {

  private T[] array;

  @SuppressWarnings("unchecked")

  public GenericArray(int sz) {

 // 由于擦除的实现,这里进行强制类型转换并不会抛出异常

    array = (T[])new Object[sz];

  }

  public void put(int index, T item) {

    array[index] = item;

  }

  public T get(int index) { return array[index]; }

  // Method that exposes the underlying representation:

  public T[] rep() { return array; }        

  public static void main(String[] args) {

    GenericArray<Integer> gai =

      new GenericArray<Integer>(10);

    // This causes a ClassCastException:

    Integer[] ia = gai.rep();

    // This is OK:

    Object[] oa = gai.rep();

  }

} ///:~

 

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 

Java 的擦除使泛型丢失了一些能力:任何运行时需要知道确切类型的操作都无法工作所以无法直接创建泛型数组。为此,我编写了下面的代码进行测试。

 

//: arrays/ArrayOfGenerics.java

package arrays;

import java.util.*;

 

class Bottle<T>{}

class Water{}

 

class Fruit{}

class Apple extends Fruit{}

 

 

public class ArrayOfGenerics {

 

  static <T> void f(T a){

  // 无法创建泛型对象

  T var = new T();

  // 无法创建泛型数组

  T[] array = new T[SIZE];

  // 错误!不能创建泛型数组

  ArrayList<T>[] list0 = new ArrayList<T>[10];

  // 仍然错误! 由于擦除的原因,ArrayList<String> 仍然是泛型的。

  ArrayList<String>[] list1 = new ArrayList<String>[10];

  // 和上面犯了一样的错误。Bottle<Water>仍然是泛型

  Bottle<Water>[] bottle = new Bottle<Water>[10];

 

  // 但是通过下面这种方式,可以间接地创建泛型数组

  ArrayList<String>[] ls;

  ArrayList[] l = new ArrayList[10];

  ls = (ArrayList<String>[])l;

  ls[0] = new ArrayList<String>();

 

  List<String>[] ls2;

  List[] l2 = new List[10];

  ls2 = (List<String>[])l2;

  // 自动向上转型

  ls2[0] = new ArrayList<String>();

 

  return;

  }

 

  static void castTest(){

  // 自动向上转型

  Fruit[] fruits = new Apple[10];

  // 强制向下转型, 由于 fruits 实现上Apple数组,所以没有问题

  Apple[] apples = (Apple[])fruits;

  apples[0] = new Apple();

  // 强制向下转型,抛出  ClassCastException 异常

  Apple[] apples2 = (Apple[]) new Fruit[10];

  }

 

  @SuppressWarnings("unchecked")

  public static void main(String[] args) {

castTest();

f(new Apple());

  }

} ///:~

 

 

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

但是如果你知道这个泛型的Class对象那还是有办法创建的。见下面的《Thinking in java》书中的代码,其中我做了一些修改和注释。

 

 

//: net/mindview/util/Generated.java

package net.mindview.util;

import java.util.*;

 

public class Generated {

  // Fill an existing array:

  public static <T> T[] array(T[] a, Generator<T> gen) {

  a.getClass();

    return new CollectionData<T>(gen, a.length).toArray(a);

  }

  // Create a new array:

  @SuppressWarnings("unchecked")

  public static <T> T[] array(Class<T> type,

      Generator<T> gen, int size) {

  // 测试如果数组 a 的容量不足或过大的情况

    T[] a =

      (T[])java.lang.reflect.Array.newInstance(type,

                      size);

    return new CollectionData<T>(gen, size).toArray(a);

  }

 

  // 和上面的  array()进行比较 . 会抛出 ClassCastException 异常

  @SuppressWarnings("unchecked")

  public static <T> T[] arrayWithErrorCast(Class<T> type,

      Generator<T> gen, int size) {

  // 错误,不能生成泛型数组

  // T[] arr = new T[size];

  List<T> a = new ArrayList<T>();

  for(int i = 0; i < size; i++){

a.add(gen.next());

  }

  // 这里的强制类型转换会抛出 ClassCastException 异常

  return (T[])a.toArray();

  }

 

  // 调整泛型数组的大小 。 会抛出 ClassCastException 异常

  @SuppressWarnings("unchecked")

  public static <T> T[] arrayAdjust(T[] a) {

  T[] newArray = (T[])java.lang.reflect.Array.newInstance(

  a.getClass(), a.length + 1);

    return newArray;

  }

 

  public static void main(String[] args){

  Integer[] ins = array(Integer.class,

      new RandomGenerator.Integer(), 10);

  for(int i = 0; i < ins.length; i++)

  System.out.print(ins[i] + " ");

  System.out.println();

 

  // 运行时会抛出异常。ClassCastException 异常

  // Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;

  Integer[] inssnd = arrayWithErrorCast(Integer.class,

      new RandomGenerator.Integer(), 10);

  for(int i = 0; i < inssnd.length; i++)

  System.out.print(inssnd[i] + " ");

 

  // 运行时会抛出异常。ClassCastException 异常

  // Ljava.lang.Integer; cannot be cast to [Ljava.lang.Integer;

  // 比较奇怪的是异常信息: Integer 不能强制转换为 Integer!

  ins = arrayAdjust(ins);

  }

} ///:~

 

 

我添加一个 arrayWithErrorCast 方法进行测试,把 ArrayList  public Object[] toArray() 方法返回的 Object[] 数组强制转换为泛型数组 T[],果然不出意外这样强制转换会抛出 ClassCastException 异常!

 

作者使用 Object java.lang.reflect.Array.newInstance(Class<?> componentType, int length) 成功创建了泛型数组。然后就可以通过ArrayList( CollectionData 派生于 ArrayList ) public <T> T[] toArray(T[] a) 方法填充数组了。

 

问题:如果参数 a的容量过小或过大会有什么问题?呵呵经过测试和文档描述一致。过小数组的容量会被重新调整,过大则剩余的元素被置为null

 

public <T> T[] toArray(T[] a)

Returns an array containing all of the elements in this list in the correct order; the runtime type of the returned array is that of the specified array. If the list fits in the specified array, it is returned therein. Otherwise, a new array is allocated with the runtime type of the specified array and the size of this list.

If the list fits in the specified array with room to spare (i.e., the array has more elements than the list), the element in the array immediately following the end of the collection is set to null. This is useful in determining the length of the list only if the caller knows that the list does not contain any null elements.

 

 

可是另外一个问题又来了,如果容量过小,那么ArrayList<T> 是如何重新创建一个新的泛型数组的? 那会不会从传递的 T[] a 参数中通过 getClass() 方法获取 Class 对象,然后再使用 java.lang.reflect.Array.newInstance 生成呢? 我也做了测试,见arrayAdjust, 很遗憾还是会抛出 ClassCastException 异常,更奇怪的是它说Integer 不能强制转换为 Integer!

 

我只能猜测  public <T> T[] toArray(T[] a) 方法进行了某种特殊处理!