Wednesday, January 16, 2013

Generics : Array Creation

How do you write a method to convert a generic collection to an Array ? A naive implementation would be:
 
public static <T> T[] convertToArray(Collection<T> c) {

   T[] a = new T[c.size()] ; // compilation error
   int i = 0 ;
   for (T x : c) {
      a[i++] = x ;

   }
     
} 
The code does not compile because the type of Array is required to be able to create an array. Array is what is known as a reifiable type. A type is reifiable if its type information is available at runtime. Any java class or primitive type is reifiable.
Generics on the other hand are implemented by erasure - that is the type information is erased and runtime uses casts to get appropriate behavior. So while
 
List<T> a = new ArrayList<T> () ; 
works because T is erased. Under the hood, just an ArrayList() is created and casts added when getting T. However,
 
T[] a = new T[size] ; // compile error
 
will not work because for arrays type information is required.
The solution is to use reflection, which is what you would if you wanted to dynamically create an instance of any reifiable type like a plain java class. The method signature in our example changes a little to take the array type as an additional parameter. Since it is easier to pass in the required array
 
public static <T> T[] convertToArray(Collection<T> c, T arry) {
   if (arry.length < c.size()) {

      arry = (T[]) java.lang.reflect.Array.newInstance(
                             arry.getClass().getComponentType(),c.size()) ;

      int i = 0 ;
      for (T x : c) {
          a[i++] = x ;
      }
   }
     
} 
The newInstance method on Array creates an array of the required type. getComponentType returns the type of elements of the array. This is analagous to using reflection to a create an instance of class K. You would do
 
K.getClass.newInstance() ;
 
In summary, in generic methods, you can use new operator to create non-reifiable types (eg List<T>) because the type information is erased during compilation (List is created). But for reifiable type, you need to use reflection because the type information is required and cannot be erased