How to create a generic array in Java?

Viewed 944230

Due to the implementation of Java generics, you can't have code like this:

public class GenSet<E> {
    private E a[];

    public GenSet() {
        a = new E[INITIAL_ARRAY_LENGTH]; // error: generic array creation
    }
}

How can I implement this while maintaining type safety?

I saw a solution on the Java forums that goes like this:

import java.lang.reflect.Array;

class Stack<T> {
    public Stack(Class<T> clazz, int capacity) {
        array = (T[])Array.newInstance(clazz, capacity);
    }

    private final T[] array;
}

But I really don't get what's going on.

31 Answers

I have to ask a question in return: is your GenSet "checked" or "unchecked"? What does that mean?

  • Checked: strong typing. GenSet knows explicitly what type of objects it contains (i.e. its constructor was explicitly called with a Class<E> argument, and methods will throw an exception when they are passed arguments that are not of type E. See Collections.checkedCollection.

    -> in that case, you should write:

    public class GenSet<E> {
    
        private E[] a;
    
        public GenSet(Class<E> c, int s) {
            // Use Array native method to create array
            // of a type only known at run time
            @SuppressWarnings("unchecked")
            final E[] a = (E[]) Array.newInstance(c, s);
            this.a = a;
        }
    
        E get(int i) {
            return a[i];
        }
    }
    
  • Unchecked: weak typing. No type checking is actually done on any of the objects passed as argument.

    -> in that case, you should write

    public class GenSet<E> {
    
        private Object[] a;
    
        public GenSet(int s) {
            a = new Object[s];
        }
    
        E get(int i) {
            @SuppressWarnings("unchecked")
            final E e = (E) a[i];
            return e;
        }
    }
    

    Note that the component type of the array should be the erasure of the type parameter:

    public class GenSet<E extends Foo> { // E has an upper bound of Foo
    
        private Foo[] a; // E erases to Foo, so use Foo[]
    
        public GenSet(int s) {
            a = new Foo[s];
        }
    
        ...
    }
    

All of this results from a known, and deliberate, weakness of generics in Java: it was implemented using erasure, so "generic" classes don't know what type argument they were created with at run time, and therefore can not provide type-safety unless some explicit mechanism (type-checking) is implemented.

This is covered in Chapter 5 (Generics) of Effective Java, 2nd Edition, item 25...Prefer lists to arrays

Your code will work, although it will generate an unchecked warning (which you could suppress with the following annotation:

@SuppressWarnings({"unchecked"})

However, it would probably be better to use a List instead of an Array.

There's an interesting discussion of this bug/feature on the OpenJDK project site.

Java generics work by checking types at compile time and inserting appropriate casts, but erasing the types in the compiled files. This makes generic libraries usable by code which doesn't understand generics (which was a deliberate design decision) but which means you can't normally find out what the type is at run time.

The public Stack(Class<T> clazz,int capacity) constructor requires you to pass a Class object at run time, which means class information is available at runtime to code that needs it. And the Class<T> form means that the compiler will check that the Class object you pass is precisely the Class object for type T. Not a subclass of T, not a superclass of T, but precisely T.

This then means that you can create an array object of the appropriate type in your constructor, which means that the type of the objects you store in your collection will have their types checked at the point they are added to the collection.

The example is using Java reflection to create an array. Doing this is generally not recommended, since it isn't typesafe. Instead, what you should do is just use an internal List, and avoid the array at all.

I found a sort of a work around to this problem.

The line below throws generic array creation error

List<Person>[] personLists=new ArrayList<Person>()[10];

However if I encapsulate List<Person> in a separate class, it works.

import java.util.ArrayList;
import java.util.List;


public class PersonList {

    List<Person> people;

    public PersonList()
    {
        people=new ArrayList<Person>();
    }
}

You can expose people in the class PersonList thru a getter. The line below will give you an array, that has a List<Person> in every element. In other words array of List<Person>.

PersonList[] personLists=new PersonList[10];

I needed something like this in some code I was working on and this is what I did to get it to work. So far no problems.

According to vnportnoy the syntax

GenSet<Integer> intSet[] = new GenSet[3];

creates an array of null references, to be filled as

for (int i = 0; i < 3; i++)
{
   intSet[i] = new GenSet<Integer>();
}

which is type safe.

You could create an Object array and cast it to E everywhere. Yeah, it's not very clean way to do it but it should at least work.

try this.

private int m = 0;
private int n = 0;
private Element<T>[][] elements = null;

public MatrixData(int m, int n)
{
    this.m = m;
    this.n = n;

    this.elements = new Element[m][n];
    for (int i = 0; i < m; i++)
    {
        for (int j = 0; j < n; j++)
        {
            this.elements[i][j] = new Element<T>();
        }
    }
}

An easy, albeit messy workaround to this would be to nest a second "holder" class inside of your main class, and use it to hold your data.

public class Whatever<Thing>{
    private class Holder<OtherThing>{
        OtherThing thing;
    }
    public Holder<Thing>[] arrayOfHolders = new Holder<Thing>[10]
}

If you really want to wrap a generic array of fixed size you will have a method to add data to that array, hence you can initialize properly the array there doing something like this:

import java.lang.reflect.Array;

class Stack<T> {
    private T[] array = null;
    private final int capacity = 10; // fixed or pass it in the constructor
    private int pos = 0;

    public void push(T value) {
        if (value == null)
            throw new IllegalArgumentException("Stack does not accept nulls");
        if (array == null)
            array = (T[]) Array.newInstance(value.getClass(), capacity);
        // put logic: e.g.
        if(pos == capacity)
             throw new IllegalStateException("push on full stack");
        array[pos++] = value;
    }

    public T pop() throws IllegalStateException {
        if (pos == 0)
            throw new IllegalStateException("pop on empty stack");
        return array[--pos];
    }
}

in this case you use a java.lang.reflect.Array.newInstance to create the array, and it will not be an Object[], but a real T[]. You should not worry of it not being final, since it is managed inside your class. Note that you need a non null object on the push() to be able to get the type to use, so I added a check on the data you push and throw an exception there.

Still this is somewhat pointless: you store data via push and it is the signature of the method that guarantees only T elements will enter. So it is more or less irrelevant that the array is Object[] or T[].

Related