-2

How to make following code completely generic?

Why I'm not allowed to write:

private E[]store = new E[length];

import java.util.Arrays;
public class MyArrayList<E> {
    private int size = 0;
    private int length = 10;

    private Object [] store = new Object[length];
        public E get(int index){
        return (E)store[index];
    }

    public void add(E item){
        if(size >= store.length -5){
            increment(store);
        }
        store[size++] = item;
    }

    private <T> void increment(T[] store2) {
        store = Arrays.copyOf(store2, store2.length * 2);
    }

    public int size(){
        return size;
    }
}
Cœur
  • 32,421
  • 21
  • 173
  • 232
Aniruddha Jagtap
  • 383
  • 5
  • 14

2 Answers2

2

You can only declare array variables whose component type is a type parameter, but you cannot create the corresponding array objects. The type of E is unknown at runtime, due to type erasure. The compiler does not know how to create an array of an unknown component type.

Similarly, you cannot create an array of concrete parameterized type.

A workaround is to use Array.newInstance method:

Class<E> clazz;
E[] array = (E[])Array.newInstance(clazz, length) ;

However, this will give you an Unchecked Cast warning, because Array.newInstance returns an Object, and you are casting it to E[]. You can suppress the warning using @SuppressWarnings annotation. But, you need to pass the clazz argument in that method, which should be the class type of E type parameter.


There is a major difference between an array and generic types - i.e, arrays are reified (As already stated by @Marko in his answer). I would add a paragraph, from the book Effective Java - Item 29 on this topic:

This means that arrays know and enforce their element types at runtime. As noted above, if you try to store a String into an array of Long, you’ll get an ArrayStoreException. Generics, by contrast, are implemented by erasure [JLS, 4.6]. This means that they enforce their type constraints only at compile time and discard (or erase) their element type information at runtime. Erasure is what allows generic types to interoperate freely with legacy code that does not use generics (Item 23).

Because of these fundamental differences, arrays and generics do not mix well. For example, it is illegal to create an array of a generic type, a parameterized type, or a type parameter. None of these array creation expressions are legal: new List<E>[], new List<String>[], new E[]. All will result in generic array creation errors at compile time.

Rohit Jain
  • 195,192
  • 43
  • 369
  • 489
2

Array element type, unlike the generic type parameter, is a reified entity. In other words, new Integer[] creates a different kind of object than new Double[]. As opposed to that, new ArrayList<Integer>() creates exactly the same object as new ArrayList<Double>().

Since the generic type parameter is erased from the runtime code, the JVM is hopeless in instantiating the correct type of array you want it to.

Marko Topolnik
  • 179,046
  • 25
  • 276
  • 399