Java Generic Array Creation: Why It Fails and How to Fix It
Explains why Java forbids generic array creation and shows practical workarounds using List, reflection, and unchecked casts.
Java does not allow generic array creation, so new T[10] does not compile. This article explains why the language forbids it and shows the practical workarounds available when you need array-like storage in generic code.
The Compile-Time Error and Why It Appears
Attempting to create an array of a generic type produces a compile-time error. The simplest case looks like this:
public class Stack<T> { private T[] elements = new T[10]; // error: generic array creation }
The compiler rejects new T[10] with the message generic array creation. This is not a limitation that can be avoided with different syntax; the same error appears whether you write new T[], new E[], or any other type parameter.
The reason is that arrays and generics use different rules for type checking at runtime. An array knows its component type at runtime, and the JVM uses that information to throw an ArrayStoreException if you insert an incompatible element. Generics, on the other hand, are enforced only at compile time and are erased during compilation. The JVM has no knowledge of the type parameter T at runtime.
Because arrays are reified and generics are erased, the two features are fundamentally incompatible. The compiler cannot guarantee that the array's runtime component type matches the erased type parameter, so it refuses to generate the code.
Why Java Rejects Generic Array Creation
To understand the rejection, consider how arrays and generics handle covariance. An array of a reference type is covariant: String[] is a subtype of Object[]. This allows code like Object[] objects = new String[10]; to compile. The JVM tracks the actual component type and throws ArrayStoreException on invalid assignment.
Generics are invariant. List<String> is not a subtype of List<Object>. The erasure of List<String> is the raw type List, and the runtime cannot distinguish it from List<Integer>. If generic array creation were allowed, the compiler would have to insert a runtime check for a type that is erased, which is impossible. The unsoundness would show up later as heap pollution:
// Hypothetical, does not compile List<String>[] array = new List<String>[10]; Object[] objects = array; objects[0] = new ArrayList<Integer>(); // allowed because the runtime component type is List String value = array[0].get(0); // ClassCastException at this read
In the hypothetical example, the array's runtime component type would be the erasure List, so the array store would not reject the ArrayList<Integer>. The bad element would then surface as a ClassCastException when code reads it as a String. The only safe alternative is to forbid the creation entirely. This is a deliberate design decision that keeps the type system consistent.
Workaround 1: Use a List Instead of an Array
The most straightforward replacement for a generic array is a List<T>. A List does not need a component class at runtime: T is erased inside the list, and the compiler inserts casts when you read elements. You get type safety at compile time without the runtime component check.
public class Stack<T> { private final List<T> elements = new ArrayList<>(); public void push(T item) { elements.add(item); } public T pop() { return elements.remove(elements.size() - 1); } }
This approach avoids the generic array creation error entirely. It also gives you dynamic resizing, which is often more convenient than a fixed-size array. The main cost is a small amount of overhead for element access and method calls, but for most applications this is negligible.
Use a List when you need a resizable collection and the number of elements is not known in advance. It is also the idiomatic choice in modern Java code, where collections are preferred over arrays in public APIs.
Workaround 2: Create the Array with Reflection
If you must return an array of type T[], you can create it at runtime using Array.newInstance. This method takes the component type and the length, and it returns an Object. You then cast the result to T[], which produces an unchecked warning.
public class Stack<T> { private T[] elements; @SuppressWarnings("unchecked") public Stack(Class<T> type, int capacity) { elements = (T[]) Array.newInstance(type, capacity); } }
The caller must supply the Class<T> object because the type parameter is erased. The cast is unchecked, meaning the compiler cannot verify that the runtime type of the array matches T. However, the array is correct because you passed the exact class object.
This approach works when the component type has a concrete class object, such as String.class or Integer.class. It cannot create an array whose component type is itself parameterized, such as List<String>, because there is no class literal for that type. The tradeoff is that the caller must pass the class object explicitly, and the responsibility for type correctness shifts to the caller.
This approach is useful when you need a real array for performance reasons or to interoperate with an API that requires an array.
Workaround 3: Unchecked Cast from Object Array
A simpler but less safe alternative is to create an Object[] and cast it to T[]. This is a common pattern in older code.
public class Stack<T> { @SuppressWarnings("unchecked") private T[] elements = (T[]) new Object[10]; }
This compiles with an unchecked warning because the cast from Object[] to T[] is not verifiable. The array's runtime type is Object[], not T[]. If the array stays private and is only used through methods that accept T, this can be acceptable. If it escapes, code can store a non-T object in it, and a ClassCastException can occur when that object is later read as T. A direct cast to a concrete array type such as String[] would also fail because the runtime component type is Object, not String.
Use this pattern only when the array is kept private and never exposed outside the class. It is a pragmatic compromise, but it should be used sparingly.
Choosing Between Array and List for Generic Data
The decision between array and list depends on your requirements. The following table summarizes the tradeoffs:
| Criterion | Array | List |
|---|---|---|
| Type safety | Reified, checked at runtime | Erased, checked at compile time |
| Generic support | Not allowed | Fully supported |
| Runtime overhead | Lower, fixed size | Slightly higher, resizable |
| Interop with APIs | Required for varargs and legacy | Preferred in modern code |
Use an array when you have a fixed size and need the lowest possible overhead. Use a List when you need generics, dynamic resizing, or the convenience of the collections framework. For generic data, a List is almost always the better choice because it avoids the workarounds described above.
Runtime Behavior and Type Safety Tradeoffs
The workarounds have different runtime characteristics. Reflection-based creation produces an array whose runtime component type matches the requested class. The unchecked cast from Object[] produces an array with a different runtime type, which can lead to heap pollution if the array escapes the class.
The reflection approach is safer because the array's component type is correct. The Object[] approach is faster to write but relies on the array staying private. If you must return an array from a generic method, the reflection approach is the correct one. The Object[] cast is a shortcut that should be documented and isolated.
There is also a subtle interaction with varargs. A generic varargs method you write, such as a helper method T[] toArray(T... args), creates an array whose component type is the erasure of T. The compiler warns about possible heap pollution unless you annotate the method with @SafeVarargs. This is a separate issue, but it shows why the language designers chose to forbid direct generic array creation.
In practice, the best approach is to avoid arrays in generic code altogether. Use List<T> and only fall back to reflection when an array is required by an external API. The unchecked cast from Object[] is a last resort that should be confined to a single method and clearly commented.