Generic Array Creation

Arrays of generic types are often hard to create and handle. I’ll show you how to create an array of a class with a generic parameter.

The Generic Type

First we need some class that has a generic type parameter:

public class Generic<T> {
  final T foo;
 
  public Generic(T foo) { this.foo = foo; }
 
  @Override
  public String toString() {
    return String.valueOf(foo);
  }
}

Instances of Generic

Then we need some instances that we can put into an Array:

final Generic<String> hello = new Generic<>("Hello");
final Generic<String> world = new Generic<>("World");

Creation of the Arrays

<?> instead of <String>:

That’s easy, but we won’t know the type. You can also remove <?> and just use the raw type.

Generic<?>[] a = { hello, world };

Raw and unchecked

You can cast a raw array, but this is an unchecked cast.

@SuppressWarnings("unchecked")
Generic<String>[] a = new Generic[] { hello, world };

Arrays.copyOf(…)

Creates a copy, length must be specified, unchecked.

@SuppressWarnings("unchecked")
Generic<String>[] a = Arrays.copyOf(new Object[] { hello, world }, 2, Generic[].class);

Array.newInstance(…)

Can’t be initialized by a list, length must be specified, unchecked.

@SuppressWarnings("unchecked")
Generic<String>[] a = (Generic<String>[]) Array.newInstance(Generic.class, 2);
a[0] = hello;
a[1] = world;

Arrays.asList(…).toArray()

Allocates a new array, unchecked. According to the API documentation you can’t be sure that the returned array is of type Generic[], it could be an Object[].

@SuppressWarnings("unchecked")
Generic<String>[] a = (Generic[]) Arrays.asList(hello, world).toArray();

Arrays.asList(…).toArray(new Generic[#])

You must give the size or it will create another array.

// Uses given array:
Generic<String>[] a = Arrays.asList(hello, world).toArray(new Generic[2]);    // Creates an new array of size 2:
Generic<String>[] a = Arrays.asList(hello, world).toArray(new Generic[0]);

asArray(…)

Method must be defined an called.

// This is inside some class called "SomeClass"  
/** @see Arrays#asList(Object...) */  
@SafeVarargs  
static <T> T[] asArray(T... elements) { return elements; }
// Type defined by parameters and by type of variable (implicit):
Generic<String>[] a = SomeClass.asArray(hello, world);
// With explicit type declaration:
Generic<String>[] a2 = SomeClass.<Generic<String>>asArray(hello, world);

Arrays.asList(…)

This is not an Array, but Collection are in many cases much better than Arrays. And it is actually backed by an array, so performance is nearly as good. I just put this here because this really is the best way to do it.

List<Generic<String>> a = Arrays.asList(hello, world);

Why unchecked is a Problem

Due to Type Erasure you will have a hard time debugging your code if anything goes wrong. Note that "Hello" is a String but "World" is a StringBuilder.

final Generic<CharSequence> hello = new Generic<>("Hello");
final Generic<CharSequence> world = new Generic<>(new StringBuilder("World"));
 
@SuppressWarnings("unchecked")
Generic<String>[] a = new Generic[] { hello, world };
System.out.println(Arrays.toString(a)); // This is ok.
Generic<String> a1 = a[1]; // Still ok.
System.out.println(a1); // Even this works.
System.out.println(a1.foo.toUpperCase()); // ClassCastException!!

PS: Type safe arrays

In Java an array isn’t really type safe. For some reason FindBugs and CheckStyle both have no check for that (or I just couldn’t find it). I wrote a feature request which explains the problem:
FindBugs – Feature Request: Type Safe Arrays

Leave a Reply

Your email address will not be published. Required fields are marked *