skip to content

Why does Java forbid creating arrays of a parameterized type like new List<String>[10], and how do you work around it?

level: seniorimportance: should knowfreq 48%

answer

  1. Arrays reified+covariant; generics erased+invariant
  2. Store-check needs the real component type
  3. new List<String>[10] = generic array creation error
  4. Allowed: List<?>[], raw List[], declaring List<String>[]
  5. Prefer List<List<String>> over the array

basics

~20 s

Arrays remember their element type at runtime and check every store against it. A List<String>[] can't do that check (the <String> is erased), so Java bans new List<String>[10] to keep the array's store-check honest. Workarounds: use new List<?>[], or better, use a List<List<String>>.

solid answer

~40 s

Arrays are reified and covariant: at runtime an array knows its component type and throws ArrayStoreException if you store the wrong thing. Parameterized types are erased, so a List<String>[] could not perform that runtime check — the <String> isn't there to check. Allowing it would silently defeat array store safety, so the language makes generic array *creation* (new List<String>[10]) a compile error; the component type of a created array must be reifiable. You can still declare a variable of type List<String>[] (the type exists), and you can create arrays of unbounded-wildcard parameterizations: new List<?>[10] is legal. Common workarounds: create new List<?>[] (or new ArrayList[]) and cast with a suppressed unchecked warning, or — preferred — avoid arrays of generics entirely and use a collection such as List<List<String>>, which is fully type-safe.

code

java · 14 lines
java
// Compile error: generic array creation
// List<String>[] bad = new List<String>[10];

// Legal: unbounded wildcard component is reifiable
List<?>[] ok = new List<?>[10];

// Pragmatic workaround with a narrowly-scoped unchecked cast
@SuppressWarnings("unchecked")
List<String>[] arr = (List<String>[]) new List<?>[10];
arr[0] = List.of("hi");

// Preferred: avoid arrays of generics entirely
List<List<String>> lists = new ArrayList<>();
lists.add(List.of("hi"));

go deeper

for a junior

Knows that new List<String>[10] doesn't compile and that you usually use a List instead.

for a middle

Explains it's because the type argument is erased, and knows new List<?>[10] is allowed and you can still declare List<String>[].

for a senior

Walks through the covariance + store-check leak that motivates the ban, and gives the unchecked-cast and prefer-collections workarounds.

for a principal

Discusses the deeper arrays-reified-vs-generics-erased design tension, heap pollution / @SafeVarargs, and library patterns (Class<T> token, Array.newInstance) for generic containers.

## Two type systems that disagree Java has **two** different ways type information is handled at runtime, and they clash here. **Arrays are reified and covariant.** Every array object carries its **component type** at runtime. When you store into an array, the JVM performs a runtime check; if the value doesn't match, it throws `ArrayStoreException`. Arrays are also *covariant*: `String[]` is a subtype of `Object[]`. That covariance is what *requires* the runtime check: ```java Object[] a = new String[3]; // legal: arrays are covariant a[0] = 42; // compiles, but throws ArrayStoreException at runtime ``` The array remembers it is really a `String[]`, so the bad store is caught. **Generics are erased and invariant.** `List<String>` is erased to `List` at runtime, and `List<String>` is *not* a subtype of `List<Object>`. There is no runtime type argument to check. ## Why generic array creation is forbidden Suppose `new List<String>[10]` were allowed. Then: ```java List<String>[] lsa = new List<String>[10]; // pretend this compiled Object[] oa = lsa; // array covariance List<Integer> li = List.of(42); oa[0] = li; // store-check: is li a List? YES (erased!) -> allowed String s = lsa[0].get(0); // ClassCastException at runtime ``` The array's runtime store-check only knows the *erased* component type `List`. `li` IS a `List`, so the check passes — and a `List<Integer>` ends up in a `List<String>[]`. The whole point of the store-check (and of generics) is defeated, and you get an unexpected `ClassCastException` far from the bug. To prevent this, the JLS requires the **component type of an array-creation expression to be reifiable**. `List<String>` is non-reifiable, so `new List<String>[10]` is a compile-time error ('generic array creation'). ## What IS allowed - **Declaring** the type: `List<String>[] x;` is fine — the type exists, you just can't `new` it directly. - **Unbounded wildcard component**: `new List<?>[10]` compiles, because `List<?>` is reifiable. - **Arrays of the raw type**: `new List[10]` / `new ArrayList[10]` compile (with a raw-type warning). - `T[]` cannot be created with `new T[n]` either — `T` is non-reifiable. ## Workarounds (least to most preferred) 1. **Create a wildcard/raw array and cast**, suppressing the unchecked warning at the narrowest scope: ```java @SuppressWarnings("unchecked") List<String>[] arr = (List<String>[]) new List<?>[10]; ``` This works but the cast is unchecked — you take responsibility that you'll only ever put `List<String>` in. 2. **Generic class needing T[]:** accept the component `Class<T>` and use `Array.newInstance(clazz, n)` (reflection), or store as `Object[]` internally and cast on the way out (as `ArrayList` does). 3. **Best: don't use arrays of generics.** Use a collection: `List<List<String>> lists = new ArrayList<>();` is fully type-safe with no warnings. Effective Java explicitly recommends preferring lists to arrays when generics are involved, precisely because of this mismatch. ## Varargs caveat Generic varargs (`void m(List<String>... args)`) create a non-reifiable array under the hood, producing the 'possible heap pollution' warning. Annotate with `@SafeVarargs` only when the method genuinely doesn't store into or leak the array.

  • Is new List<?>[10] legal? Why?
    Yes. List<?> is reifiable (unbounded wildcard loses nothing to erasure), and array creation only requires a reifiable component type.
  • What is @SafeVarargs for?
    Generic varargs secretly create a non-reifiable array (heap-pollution warning). @SafeVarargs suppresses it, asserting the method neither stores into nor leaks the varargs array.

saying these in an interview costs you the question

  • Saying you can never have a List<String>[] variable — you can declare it, you just can't new it
  • Claiming the workaround cast is fully type-safe — it's an unchecked cast, you assume the risk
  • Forgetting array covariance is the reason the runtime store-check exists
  • Confusing ClassCastException (what would leak) with ArrayStoreException (what arrays normally throw)

context