Question
In Java, direct generic array creation is not allowed because of how generics are implemented. For example, this code does not compile:
public class GenSet<E> {
private E[] a;
public GenSet() {
a = new E[10]; // Error: generic array creation
}
}
How can this be implemented while preserving type safety?
One common solution uses reflection:
import java.lang.reflect.Array;
class Stack<T> {
private final T[] array;
public Stack(Class<T> clazz, int capacity) {
array = (T[]) Array.newInstance(clazz, capacity);
}
}
Why does this approach work, and what is happening behind the scenes in Java?
Short Answer
By the end of this page, you will understand why new T[] or new E[] is illegal in Java, how type erasure causes this limitation, how Array.newInstance(...) works, and when you should prefer arrays versus collections like ArrayList. You will also see how to use reflective array creation carefully and safely in real code.
Concept
Java generics and Java arrays enforce type information in different ways.
- Arrays are reified: they know their element type at runtime.
- A
String[]knows it is aString[]while the program is running. - Because of that, Java can reject invalid stores at runtime.
- A
- Generics are erased: most generic type information does not exist at runtime.
List<String>andList<Integer>both become justListat runtime.
This mismatch is the reason Java does not allow:
new T[10]
When Java compiles generic code, T is erased. At runtime, Java would not know what exact array type to create. But arrays require a real runtime component type such as String.class, Integer.class, or Object.class.
Why Java forbids generic array creation
Suppose Java allowed this:
T[] data = new T[10];
At runtime, what would T be? Because of type erasure, the JVM usually does not know. If Java created an Object[] behind the scenes, that could break type safety because the variable would appear to be T[], but the actual runtime array would not truly match that specific type.
So Java blocks generic array creation at compile time to avoid unsafe situations.
Why Array.newInstance(...) works
This version works:
array = (T[]) Array.newInstance(clazz, capacity);
because you explicitly pass the runtime type information using Class<T>.
For example:
new Stack<>(String.class, 5)
Now Java has the real component type: String.class. Reflection can create a real String[] at runtime.
That means:
Array.newInstance(String.class, 5)
creates an actual String[].
The cast to (T[]) is needed because Array.newInstance(...) returns Object, but in this context you know the returned object is really an array of the requested component type.
Is it fully type-safe?
It is safe if used correctly:
- The caller must pass the correct
Class<T>. - The array should stay encapsulated inside the class when possible.
- You should avoid exposing it in ways that let callers misuse it.
In many cases, Java developers avoid this complexity entirely by using collections such as ArrayList<T> instead of T[].
Mental Model
Think of a generic type like T as a label that exists mainly during compilation, while an array is a box that must have its item type stamped on it at runtime.
- A generic says: “Trust me, this class works with some type.”
- An array says: “I need the exact item type right now.”
So new T[10] fails because the array asks, “What exact type should I stamp on this box?” and generics reply, “That information was erased.”
Passing Class<T> is like handing the factory a blueprint:
- Without
Class<T>: “Build me a box for some unknown type.” - With
Class<T>: “Build me a box forStringitems.”
Then the factory can produce a real String[].
Syntax and Examples
The common pattern is to pass a Class<T> token into the constructor and use reflection to create the array.
import java.lang.reflect.Array;
public class GenericBuffer<T> {
private final T[] items;
@SuppressWarnings("unchecked")
public GenericBuffer(Class<T> clazz, int size) {
items = (T[]) Array.newInstance(clazz, size);
}
public void set(int index, T value) {
items[index] = value;
}
public T get(int index) {
return items[index];
}
public int size() {
return items.length;
}
}
Usage:
public class Main {
public static void main(String[] args) {
GenericBuffer<String> buffer = new GenericBuffer<>(String.class, 3);
buffer.set(0, "A");
buffer.set(1, "B");
System.out.println(buffer.get(0));
System.out.println(buffer.size());
}
}
What this does
String.classcarries runtime type information.Array.newInstance(String.class, 3)creates a realString[].- The cast to
T[]tells the compiler that the object is intended to be treated as an array ofT.
Alternative: use Object[] internally
Another common approach is to store values in Object[] and cast when reading:
public class SimpleStack<T> {
private final Object[] items;
private int size = 0;
public SimpleStack(int capacity) {
items = new Object[capacity];
}
public void push(T value) {
items[size++] = value;
}
@SuppressWarnings("unchecked")
public T pop() {
return (T) items[--size];
}
}
This is also common, especially in the JDK, as long as the class carefully controls what gets stored.
Alternative: use a collection
If you do not specifically need an array, prefer:
import java.util.ArrayList;
import java.util.List;
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
This is simpler and avoids the generic-array problem entirely.
Step by Step Execution
Consider this code:
import java.lang.reflect.Array;
public class Demo<T> {
private final T[] values;
@SuppressWarnings("unchecked")
public Demo(Class<T> clazz, int size) {
values = (T[]) Array.newInstance(clazz, size);
}
public void setFirst(T value) {
values[0] = value;
}
public T getFirst() {
return values[0];
}
}
Used like this:
Demo<String> demo = new Demo<>(String.class, 2);
demo.setFirst("hello");
String result = demo.getFirst();
Step by step
1. Demo<String> is created
At compile time, T is treated as String in this usage.
2. String.class is passed in
The constructor receives runtime type information that generics alone do not keep.
3. Array.newInstance(clazz, size) runs
This becomes effectively:
Array.newInstance(String.class, 2)
That creates a real runtime array of type String[].
4. The result is cast to T[]
Reflection returns Object, so the compiler requires a cast:
(T[]) ...
In this case, the object really is a String[], so the cast is valid.
5. demo.setFirst("hello")
The value is stored in the array.
6. demo.getFirst() returns a String
Because T is String for this object, the method returns a String to the caller.
Why runtime array checks still matter
Arrays check inserted values at runtime. For example:
Object[] arr = new String[2];
arr[0] = "ok";
// arr[1] = 123; // ArrayStoreException at runtime
That is possible because arrays remember they are really String[].
Generics do not work like that, because their type arguments are erased.
Real World Use Cases
Generic arrays appear less often than generic collections, but they still matter in some practical situations.
Common use cases
- Custom data structures
- Stacks
- Queues
- Ring buffers
- Heaps
- Performance-sensitive code
- Arrays can be faster and more memory-efficient than some collection wrappers.
- Libraries that need fixed-size storage
- Internal buffers for parsing, caching, or batch processing.
- Bridging with APIs that require arrays
- Some older Java APIs or lower-level code still work naturally with arrays.
Example scenarios
Fixed-capacity stack
A parser or expression evaluator may use a generic stack backed by an array.
Message buffer
A messaging system may keep a fixed-size array of events before flushing them.
Object pools
A framework may store reusable objects in an internal array for speed.
In all of these cases, developers often either:
- use
Object[]internally, or - create a typed array with
Array.newInstance(...)if a realT[]is needed.
Real Codebase Usage
In real projects, developers usually choose one of three patterns.
1. Prefer collections when possible
This is the most common approach:
List<T> items = new ArrayList<>();
Why teams prefer it:
- no generic array creation problem
- flexible resizing
- clearer APIs
- fewer unchecked casts
2. Use Object[] internally with controlled casts
This is common in internal implementations of data structures.
public class Bag<T> {
private final Object[] items;
private int size;
public Bag(int capacity) {
items = new Object[capacity];
}
public void add(T item) {
items[size++] = item;
}
@SuppressWarnings("unchecked")
public T get(int index) {
return (T) items[index];
}
}
Pattern benefits:
- simple implementation
- no need to pass
Class<T>around - safe if insertion is fully controlled by the class
3. Use Class<T> when a real T[] is required
This is useful when:
- the API must return
T[] - code integrates with array-based APIs
- you need the true runtime array component type
Related patterns in production code
Validation
Developers validate sizes before creating arrays:
if (capacity < 0) {
throw new IllegalArgumentException("capacity must be >= 0");
}
Encapsulation
The backing array is usually private so callers cannot misuse it.
Guard clauses
Methods often check bounds early:
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException();
}
Suppressing warnings carefully
Unchecked casts are kept in the smallest possible scope:
@SuppressWarnings("unchecked")
T[] temp = (T[]) Array.newInstance(clazz, capacity);
That makes it easier to audit safety.
Common Mistakes
1. Trying to write new T[] directly
Broken code:
public class Box<T> {
private T[] items = new T[10];
}
Why it fails:
- Java does not know
Tat runtime. - Generic type parameters are erased.
How to avoid it:
- use
Array.newInstance(...), or - use
Object[], or - use a collection like
ArrayList<T>.
2. Assuming generics and arrays work the same way
Broken assumption:
List<String> list = new ArrayList<>();
// list knows String at runtime? No, not in the same way arrays do.
Arrays keep runtime component type information; generic type arguments usually do not.
3. Using the wrong Class<T> token
Broken code:
GenericBuffer<String> buffer = new GenericBuffer<>((Class<String>) Integer.class, 5);
This is unsafe and can lead to runtime problems.
How to avoid it:
- always pass the correct class token, such as
String.classforT = String
4. Exposing the raw backing array carelessly
Broken design:
public T[] getArray() {
return items;
}
This may let outside code modify internal state unexpectedly.
How to avoid it:
- return copies when needed
- keep the backing array private
- expose higher-level methods instead
5. Sprinkling @SuppressWarnings("unchecked") everywhere
Bad practice:
@SuppressWarnings("unchecked")
public T get(int index) {
return (T) items[index];
}
This can be okay, but only when the cast is truly safe and well-contained.
Better:
- suppress warnings only in the smallest location where you can justify safety
- document why the cast is safe
6. Forgetting that arrays are covariant
This compiles:
Object[] arr = new String[2];
But this fails at runtime:
arr[0] = 123; // ArrayStoreException
How to avoid it:
- remember that array covariance can introduce runtime errors
- prefer generics for API-level type safety
Comparisons
| Concept | Runtime type info | Compile-time type safety | Can create directly with new? | Notes |
|---|---|---|---|---|
String[] | Yes | Yes | Yes | Arrays know their component type at runtime |
T[] in generic code | Not directly | Yes at compile time | No | T is erased, so direct creation is forbidden |
Object[] backing store | Yes (Object[]) | Limited internally | Yes | Common internal implementation strategy |
List<T> | Generic type erased | Yes | Yes (new ArrayList<>()) | Usually preferred over arrays |
Array.newInstance(...) vs Object[]
| Approach | Best when | Pros | Cons |
|---|---|---|---|
Array.newInstance(clazz, size) | You need a real T[] | Preserves runtime component type | Requires Class<T> and an unchecked cast |
Object[] | Internal storage only | Simpler and common | Casts needed when reading |
ArrayList<T> | Most application code | Easiest and safest API | Not a raw array |
Arrays vs generic collections
| Feature | Array | List<T> |
|---|---|---|
| Fixed size | Yes | No, usually dynamic |
| Knows element type at runtime | Yes | No, type arguments erased |
| Works naturally with generics | Not perfectly | Yes |
| Best for most app code | Sometimes | Usually |
Cheat Sheet
Quick rules
new T[10]is illegal in Java.- Arrays are reified: they know their element type at runtime.
- Generics are erased:
Tusually is not known at runtime. - To create a real generic array, pass
Class<T>and useArray.newInstance(...). - If you only need internal storage,
Object[]is often enough. - If you do not need an array specifically, prefer
List<T>.
Core pattern
import java.lang.reflect.Array;
public class Holder<T> {
private final T[] items;
@SuppressWarnings("unchecked")
public Holder(Class<T> clazz, int size) {
items = (T[]) Array.newInstance(clazz, size);
}
}
Alternative internal pattern
public class Holder<T> {
private final Object[] items;
public Holder(int size) {
items = new Object[size];
}
@SuppressWarnings("unchecked")
public T get(int index) {
return (T) items[index];
}
}
When to use what
- Use
List<T>for most normal application code. - Use
Object[]for internal generic data structures. - Use
Array.newInstance(...)when you truly need a runtimeT[].
Watch out for
- incorrect
Class<T>tokens - unchecked casts
- exposing internal arrays
- array covariance causing
ArrayStoreException
FAQ
Why can't I create new T[] in Java?
Because generic type parameters are erased at runtime, and arrays require a real runtime component type.
Is Array.newInstance(...) type-safe?
It can be, if you pass the correct Class<T> and keep the cast and array usage controlled inside the class.
Why does Array.newInstance(...) return Object?
Reflection APIs are general-purpose. They return Object, so you must cast the result to the array type you expect.
Should I use arrays or ArrayList<T>?
Use ArrayList<T> unless you specifically need an array or fixed-size internal storage.
Why is there an unchecked cast warning?
The compiler cannot fully verify the cast from Object to T[] because of type erasure.
Can I use Object[] instead of T[]?
Yes. This is a very common implementation strategy for generic containers.
What is the difference between arrays and generics in Java?
Arrays keep element type information at runtime; generics mostly enforce type checks only at compile time.
What runtime error can arrays produce that generics avoid?
Arrays can throw ArrayStoreException if you try to store the wrong type in a runtime-typed array.
Mini Project
Description
Build a fixed-size generic stack in Java. This project demonstrates how to store values of any type in an array-backed data structure without trying to write new T[] directly. It is useful because stacks are a classic real-world data structure used in parsers, undo systems, and expression evaluation.
Goal
Create a reusable generic stack that can push, pop, peek, and report whether it is empty, using safe reflective array creation.
Requirements
- Create a generic
ArrayStack<T>class. - Accept
Class<T>and capacity in the constructor. - Store stack items in an internal array created with
Array.newInstance(...). - Implement
push,pop,peek,isEmpty, andsizemethods. - Throw an exception when pushing to a full stack or popping from an empty stack.
Keep learning
Related questions
Add External JAR Files to an IntelliJ IDEA Java Project
Learn how to add external JAR dependencies to an IntelliJ IDEA Java project using module libraries, and when to use Maven or Gradle instead.
Avoiding Java Code in JSP with JSP 2: EL and JSTL Explained
Learn how to avoid Java scriptlets in JSP 2 using Expression Language and JSTL, with examples, best practices, and common mistakes.
Call a Method After a Delay in Android Java
Learn how to run Java code after a delay in Android using Handler.postDelayed, manage the main thread, and cancel callbacks safely.