Arrays of generics

来源:互联网 发布:linux移动文件权限不够 编辑:程序博客网 时间:2024/05/21 22:55

You can't create arrays of generics. The general solution is to use an ArrayList everywhere that you are temped to create an array of generics:

 public class ListOfGenerics<T> {   private List<T> array = new ArrayList<T>();   public void add(T item) { array.add(item); }   public T get(int index) { return array.get(index); } } ///:~ 

Here you get the behavior of an array but the compile-time type safety afforded by generics.

Interesting enough, you can define a reference in a way that makes to compliler happy. For example:

 class Generic<T> {}  public class ArrayOfGenericReference {   static Generic<Integer>[] gia; } ///:~ 

The compiler accepts this without producing warnings. But you can never create an array of that exact type, so it's a little confusing. Since all arrays have the same structrue regardless of the type they hold, it seems that you should be able to create an array of Object and cast that to the desired array type. This does in fact compile, but it won't run; It produces a ClassCastException:

 public class ArrayOfGeneric {   static final int SIZE = 100;   static Generic<Integer>[] gia;   @SuppressWarnings("unchecked")   public static void main(String[] args) {     // Compiles; produces ClassCastException:     //! gia = (Generic<Integer>[])new Object[SIZE];     // Runtime type is the raw (erased) type:     gia = (Generic<Integer>[])new Generic[SIZE];     System.out.println(gia.getClass().getSimpleName());    gia[0] = new Generic<Integer>();     //! gia[1] = new Object(); // Compile-time error     // Discovers type mismatch at compile time:     //! gia[2] = new Generic<Double>();   } } /* Output: Generic[] 

The problem is that arrays keep track of there actual type, and that type is established at the point of creation of the array. So even though gia has been cast to a Generic<Integer>[], that information only exsits at compile time. At run time, it's still an array of Object, and that cause problem. The only way to successfully create an array of generic type is to create a new array of the erased type, and cast that.

Consider a simple generic wrapper arround an array:

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();   } } 

because of erasure, the runtime type of the array can only be Object[]. If we immediately cast it to T[], then at compile time the actual type of array is lost, and the compiler may miss out on some potential error checks. Because of this, it is better to use an Object[] inside the collection, and add a cast to T when you use an array element. Let's  see how that would look:

 public class GenericArray2<T> {   private Object[] array;   public GenericArray2(int sz) {     array = new Object[sz];   }   public void put(int index, T item) {     array[index] = item;   }   @SuppressWarnings("unchecked")   public T get(int index) { return (T)array[index]; }   @SuppressWarnings("unchecked")   public T[] rep() {     return (T[])array; // Warning: unchecked cast   }   public static void main(String[] args) {     GenericArray2<Integer> gai =       new GenericArray2<Integer>(10);     for(int i = 0; i < 10; i ++)       gai.put(i, i);     for(int i = 0; i < 10; i ++)       System.out.print(gai.get(i) + " ");     System.out.println();     try {       Integer[] ia = gai.rep();     } catch(Exception e) { System.out.println(e); }   } } /* Output: (Sample) 0 1 2 3 4 5 6 7 8 9 java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer; *///:~ 

Initially, this doesn't look very different, just that the cast has been moved. The internal representation is now Object[] rather than T[], when get() is called, it casts the object to T, which is in fact the correct type, so that it's safe. However, if you call rep(), it again attempts to cast the Object[] to a T[], which is still incorrect, and produces a warning at compile time and an exception at run time. 

for new code, you shoul passing a type token:

 public class GenericArrayWithTypeToken<T> {   private T[] array;   @SuppressWarnings("unchecked")   public GenericArrayWithTypeToken(Class<T> type, int sz) {     array = (T[])Array.newInstance(type, sz);   }   public void put(int index, T item) {     array[index] = item;   }   public T get(int index) { return array[index  // Expose the underlying representation:   public T[] rep() { return array; }    public static void main(String[] args) {     GenericArrayWithTypeToken<Integer> gai =       new GenericArrayWithTypeToken<Integer>(         Integer.class, 10);     // This now works:     Integer[] ia = gai.rep();   } } ///:~ 

the type token Class<T> is passed into the constructor in order to  encover from the erasure, so that we can create the actual type of array that we need.

Unfortunately, if you look at the source code in the java SE5 standard libraries, you'll see there are casts from Object arrays to parameterized types everywhere.









原创粉丝点击