How to create a generic array in Java? How to create a generic array in Java? java java

How to create a generic array in Java?


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.


You can do this:

E[] arr = (E[])new Object[INITIAL_ARRAY_LENGTH];

This is one of the suggested ways of implementing a generic collection in Effective Java; Item 26. No type errors, no need to cast the array repeatedly. However this triggers a warning because it is potentially dangerous, and should be used with caution. As detailed in the comments, this Object[] is now masquerading as our E[] type, and can cause unexpected errors or ClassCastExceptions if used unsafely.

As a rule of thumb, this behavior is safe as long as the cast array is used internally (e.g. to back a data structure), and not returned or exposed to client code. Should you need to return an array of a generic type to other code, the reflection Array class you mention is the right way to go.


Worth mentioning that wherever possible, you'll have a much happier time working with Lists rather than arrays if you're using generics. Certainly sometimes you don't have a choice, but using the collections framework is far more robust.


Here's how to use generics to get an array of precisely the type you’re looking for while preserving type safety (as opposed to the other answers, which will either give you back an Object array or result in warnings at compile time):

import java.lang.reflect.Array;  public class GenSet<E> {      private E[] a;      public GenSet(Class<E[]> clazz, int length) {          a = clazz.cast(Array.newInstance(clazz.getComponentType(), length));      }      public static void main(String[] args) {          GenSet<String> foo = new GenSet<String>(String[].class, 1);          String[] bar = foo.a;          foo.a[0] = "xyzzy";          String baz = foo.a[0];      }  }

That compiles without warnings, and as you can see in main, for whatever type you declare an instance of GenSet as, you can assign a to an array of that type, and you can assign an element from a to a variable of that type, meaning that the array and the values in the array are of the correct type.

It works by using class literals as runtime type tokens, as discussed in the Java Tutorials. Class literals are treated by the compiler as instances of java.lang.Class. To use one, simply follow the name of a class with .class. So, String.class acts as a Class object representing the class String. This also works for interfaces, enums, any-dimensional arrays (e.g. String[].class), primitives (e.g. int.class), and the keyword void (i.e. void.class).

Class itself is generic (declared as Class<T>, where T stands for the type that the Class object is representing), meaning that the type of String.class is Class<String>.

So, whenever you call the constructor for GenSet, you pass in a class literal for the first argument representing an array of the GenSet instance's declared type (e.g. String[].class for GenSet<String>). Note that you won't be able to get an array of primitives, since primitives can't be used for type variables.

Inside the constructor, calling the method cast returns the passed Object argument cast to the class represented by the Class object on which the method was called. Calling the static method newInstance in java.lang.reflect.Array returns as an Object an array of the type represented by the Class object passed as the first argument and of the length specified by the int passed as the second argument. Calling the method getComponentType returns a Class object representing the component type of the array represented by the Class object on which the method was called (e.g. String.class for String[].class, null if the Class object doesn't represent an array).

That last sentence isn't entirely accurate. Calling String[].class.getComponentType() returns a Class object representing the class String, but its type is Class<?>, not Class<String>, which is why you can't do something like the following.

String foo = String[].class.getComponentType().cast("bar"); // won't compile

Same goes for every method in Class that returns a Class object.

Regarding Joachim Sauer's comment on this answer (I don't have enough reputation to comment on it myself), the example using the cast to T[] will result in a warning because the compiler can't guarantee type safety in that case.


Edit regarding Ingo's comments:

public static <T> T[] newArray(Class<T[]> type, int size) {   return type.cast(Array.newInstance(type.getComponentType(), size));}