Question 14 · Section 20

Can you create an array of generic type

This breaks type safety — the error is not caught at compile time.

Language versions: English Russian Ukrainian

🟢 Junior Level

No, you cannot directly. Java does not allow creating arrays of generic types due to type erasure.

// ❌ Cannot do this
public class Box<T> {
    private T[] array = new T[10];  // compilation error: "generic array creation"
}

Why: Arrays in Java know their type at runtime, while generics lose their type after compilation (type erasure). These two mechanisms are incompatible.

Solutions:

// Solution 1: Array of Object with cast
public class Box<T> {
    private T[] array = (T[]) new Object[10];  // unchecked warning
}

// Solution 2: ArrayList instead of array
public class Box<T> {
    private List<T> list = new ArrayList<>();
}

// Solution 3: Pass Class<T>
public class Box<T> {
    private T[] array;

    public Box(Class<T> type, int size) {
        array = (T[]) Array.newInstance(type, size);
    }
}

🟡 Middle Level

Why is it not allowed?

Conflicting models:

  • Arrays — reified (know their type at runtime)
  • Generics — type erasure (type is lost)
// If it were allowed:
List<String>[] stringLists = new List<String>[10];
Object[] objects = stringLists;  // arrays are covariant
objects[0] = List.of(42);        // List<Integer> — compiler doesn't complain!
String s = stringLists[0].get(0); // ClassCastException!

This breaks type safety — the error is not caught at compile time.

Working solutions

1. Unchecked cast:

public class GenericStack<T> {
    private T[] elements;
    private int size = 0;

    @SuppressWarnings("unchecked")
    public GenericStack(int capacity) {
        elements = (T[]) new Object[capacity];  // unchecked warning
    }

    public void push(T item) {
        elements[size++] = item;
    }

    public T pop() {
        return elements[--size];
    }
}

2. Reflection:

public class GenericArray<T> {
    private final T[] array;

    public GenericArray(Class<T> type, int size) {
        array = (T[]) Array.newInstance(type, size);
    }

    public void set(int index, T value) {
        array[index] = value;
    }

    public T get(int index) {
        return array[index];
    }
}

// Usage
GenericArray<String> strings = new GenericArray<>(String.class, 10);
strings.set(0, "Hello");

3. Collections instead of arrays:

// Best solution in most cases
public class Box<T> {
    private final List<T> items = new ArrayList<>();

    public void add(T item) { items.add(item); }
    public T get(int index) { return items.get(index); }
}

Common mistakes

  1. Ignoring unchecked warning: ```java // ⚠️ Warning suppressed, but the problem remains T[] array = (T[]) new Object[10];

// Better — document why this is safe


2. **Heap pollution:**
```java
// Dangerous — can put wrong type
Object[] raw = new Object[10];
String[] strings = (String[]) raw;  // ClassCastException!

🔴 Senior Level

Internal Implementation

Why arrays are reified:

JVM specification:
- Arrays store component type in class file
- Runtime check on every access
- ArrayStoreException on wrong type

// Example
String[] arr = new String[10];
arr[0] = "Hello";     // OK
arr[1] = 42;          // ArrayStoreException at runtime!

Type erasure conflict:

// If generic arrays were allowed:
List<String>[] lists = new List<String>[5];
Object[] obj = lists;  // covariant
obj[0] = new ArrayList<Integer>();  // OK for compiler
String s = lists[0].get(0);         // ClassCastException!

// Array cannot check element type at runtime,
// because List<String> and List<Integer> are the same raw type after erasure

Architectural Trade-offs

Approaches to generic arrays:

Approach Pros Cons
Object[] + cast Simple Unchecked warning
Reflection Type-safe Needs Class
Collections Safe More overhead

Edge Cases

1. Generic varargs:

// Varargs are arrays!
public static <T> List<T> asList(T... items) {
    return Arrays.asList(items);
}

// ⚠️ Heap pollution warning
asList("a", "b", "c");  // OK
// But compiler warns

// @SafeVarargs fixes it
@SafeVarargs
public static <T> List<T> safeAsList(T... items) {
    return Arrays.asList(items);
}

2. Nested generic arrays:

// Even harder — nested types
public class Matrix<T> {
    // ❌ Cannot do this
    private T[][] matrix = new T[10][10];  // error

    // ✅ Via Reflection
    private T[][] matrix;

    public Matrix(Class<T> type, int rows, int cols) {
        matrix = (T[][]) Array.newInstance(type, rows, cols);
    }
}

3. ArrayList.toArray() problem:

List<String> list = List.of("a", "b", "c");

// ❌ Cannot do this
String[] array = (String[]) list.toArray();  // ClassCastException
// toArray() returns Object[]

// ✅ Correct approach
String[] array = list.toArray(new String[0]);
// Or Java 11+
String[] array = list.toArray(String[]::new);

Performance

Array vs ArrayList:
Operation         | Array        | ArrayList
------------------|--------------|----------
Access            | 1 ns         | 1 ns
Add               | N/A (fixed)  | 1-10 ns (resize)
Memory            | Minimum      | + overhead
Type check        | Runtime      | Compile time

Generic array creation:
- Object[] + cast: Zero overhead
- Reflection: ~5-10 ns per creation
- Collections: +10-20% memory

Production Experience

Java collections internal:

// ArrayList internal — uses Object[] + cast
public class ArrayList<E> {
    private transient Object[] elementData;  // NOT E[]

    @SuppressWarnings("unchecked")
    public E get(int index) {
        return (E) elementData[index];  // unchecked cast
    }
}

// HashMap — similarly
transient Node<K,V>[] table;  // array of raw Node

// This is safe because ArrayList controls all writes internally
// and does not allow elements of wrong type.

Safe generic array:

public class SafeGenericArray<T> {
    private final Object[] array;
    private final Class<T> type;

    public SafeGenericArray(Class<T> type, int size) {
        this.type = type;
        this.array = new Object[size];
    }

    public void set(int index, T value) {
        if (!type.isInstance(value)) {
            throw new IllegalArgumentException();
        }
        array[index] = value;
    }

    @SuppressWarnings("unchecked")
    public T get(int index) {
        return (T) array[index];
    }
}

Best Practices

// ✅ Use collections instead of arrays
List<T> list = new ArrayList<>();

// ✅ If you need an array — Object[] + cast
@SuppressWarnings("unchecked")
private T[] array = (T[]) new Object[size];

// ✅ @SafeVarargs for varargs methods
@SafeVarargs
public static <T> T[] merge(T[] a1, T[] a2) { }

// ✅ Reflection when you need a type-safe array
T[] array = (T[]) Array.newInstance(type, size);

// ❌ new T[]
// ❌ Ignoring unchecked warnings without justification
// ❌ Heap pollution — exposing generic array externally

🎯 Interview Cheat Sheet

Must know:

  • new T[] — compilation error: “generic array creation”
  • Reason: arrays are reified (know type at runtime), generics — type erasure (type is lost)
  • Solution 1: Object[] + unchecked cast (T[]) new Object[size]
  • Solution 2: Reflection — Array.newInstance(type, size) with Class<T> passed
  • Solution 3: Use ArrayList<T> instead of array
  • @SafeVarargs suppresses warnings for varargs methods
  • ArrayList internally uses Object[] + unchecked cast

Frequent follow-up questions:

  • Why can’t you create a generic array? — Conflict between reified arrays and type-erased generics
  • How does ArrayList store elements? — Object[] elementData + unchecked cast on get()
  • What is heap pollution? — When a generic collection contains elements of the wrong type
  • Is unchecked cast in ArrayList safe? — Yes, ArrayList controls all writes internally

Red flags (DO NOT say):

  • ❌ “Generic array can be created via unchecked cast without issues” — Heap pollution is possible
  • ❌ “Generic arrays work in Java” — Banned at compiler level
  • ❌ “ArrayList uses T[] internally” — ArrayList uses Object[], not T[]
  • ❌ “Heap pollution is a JVM error” — It’s a compile-time warning, a type safety issue

Related topics:

  • [[11. What are Generics in Java]]
  • [[13. What is type erasure]]
  • [[18. Can you use primitive types as generic parameters]]
  • [[19. What are raw types and why should you avoid them]]