What happens when trying to create an instance of generic type via new T()
Structured Java interview answer with junior, middle, and senior-level explanation.
🟢 Junior Level
You cannot create an instance of a generic type via new T(). This is a compilation error.
public class Box<T> {
private T value;
public Box() {
value = new T(); // ❌ compilation error: "cannot instantiate T"
}
}
Why: Due to type erasure, the compiler does not know what type T stands for at runtime. After compilation, T becomes Object, and new Object() is not what you want.
Solutions:
// Solution 1: Pass Class<T>
public class Box<T> {
private T value;
public Box(Class<T> type) throws Exception {
value = type.getDeclaredConstructor().newInstance();
}
}
// Solution 2: Factory (Supplier)
public class Box<T> {
private T value;
public Box(Supplier<T> factory) {
value = factory.get();
}
}
// Usage
Box<String> box1 = new Box<>(String.class);
Box<String> box2 = new Box<>(() -> "default");
🟡 Middle Level
Why is it not allowed?
Type erasure:
public class Box<T> {
public Box() {
value = new T(); // ❌ what does this mean after erasure?
}
}
// After compilation:
public class Box {
public Box() {
value = new Object(); // T -> Object
// But this is not what the developer intended!
}
}
Problem: There is no information about the constructor of T. What if T has no no-arg constructor?
Working solutions
**1. Class
public class Factory<T> {
private final Class<T> type;
public Factory(Class<T> type) {
this.type = type;
}
public T create() throws Exception {
return type.getDeclaredConstructor().newInstance();
}
}
Factory<String> stringFactory = new Factory<>(String.class);
String s = stringFactory.create(); // ""
2. Supplier approach:
public class Box<T> {
private final Supplier<T> factory;
public Box(Supplier<T> factory) {
this.factory = factory;
}
public T create() {
return factory.get();
}
}
Box<User> userBox = new Box<>(User::new);
User user = userBox.create();
3. Abstract factory method:
public abstract class Creator<T> {
protected abstract T createInstance();
public T createAndProcess() {
T instance = createInstance();
// process
return instance;
}
}
public class UserCreator extends Creator<User> {
@Override
protected User createInstance() {
return new User();
}
}
Common mistakes
- Trying to bypass via cast:
public class Box<T> { public T create() { return (T) new Object(); // ⚠️ unchecked cast // Works only if T = Object // ClassCastException for other types } } - Reflection without check:
```java
public T create(Class
type) throws Exception { return type.newInstance(); // deprecated in Java 9+ }
// ✅ Better
public T create(Class
---
## 🔴 Senior Level
### Internal Implementation
**Why the compiler forbids it:**
JLS 15.9:
- new T() is impossible because T is a type variable
- Type variables have no constructor metadata in class files.
- The JVM literally cannot emit a new instruction for T.
- After erasure T -> Object or bound ```
Reflection approach:
public T create(Class<T> type) {
try {
Constructor<T> ctor = type.getDeclaredConstructor();
return ctor.newInstance();
} catch (NoSuchMethodException e) {
// T has no no-arg constructor
throw new RuntimeException(e);
}
}
Architectural Trade-offs
Approaches to creating T:
| Approach | Pros | Cons |
|---|---|---|
| **Class |
Type-safe, simple | Needs no-arg constructor |
| **Supplier |
Flexible, any constructor | Need to pass factory |
| Abstract method | OOP style | Need a subclass |
| Unchecked cast | Simple | Unsafe, ClassCastException risk |
Edge Cases
1. Bounded type with no-arg constructor:
public class Box<T extends Runnable> {
public T create(Class<T> type) throws Exception {
// T must have a no-arg constructor
return type.getDeclaredConstructor().newInstance();
}
}
2. Constructor with parameters:
public T create(Class<T> type, String name) throws Exception {
return type.getConstructor(String.class)
.newInstance(name);
}
3. Generic array creation:
// Also not allowed
public class Box<T> {
private T[] items = new T[10]; // ❌ error
// ✅ Solution
private T[] items = (T[]) new Object[10];
}
Performance
Ways to create T:
- new T(): N/A (not allowed)
- Class.newInstance(): ~50-100 ns (deprecated)
// Approximate values. Depend on JVM, optimization level, inlining.
- Constructor.newInstance(): ~100-200 ns
- Supplier.get(): ~1-5 ns (regular call)
- Unchecked cast: ~1 ns (but unsafe)
Supplier — fastest runtime approach
Production Experience
Spring Bean Factory:
@Component
public class EntityFactory {
public <T> T create(Class<T> type) {
return applicationContext.getBean(type);
}
}
// Usage
User user = entityFactory.create(User.class);
Builder pattern:
public class Builder<T> {
private final Supplier<T> factory;
private final Map<String, Object> properties = new HashMap<>();
public Builder(Supplier<T> factory) {
this.factory = factory;
}
public Builder<T> with(String name, Object value) {
properties.put(name, value);
return this;
}
public T build() {
T instance = factory.get();
// inject properties
return instance;
}
}
User user = new Builder<>(User::new)
.with("name", "John")
.with("age", 25)
.build();
Best Practices
// ✅ Supplier for flexibility
public class Box<T> {
private final Supplier<T> factory;
public Box(Supplier<T> factory) { this.factory = factory; }
public T create() { return factory.get(); }
}
// ✅ Class<T> for simple cases
public T create(Class<T> type) {
return type.getDeclaredConstructor().newInstance();
}
// ✅ Abstract factory method
protected abstract T createInstance();
// ❌ new T()
// ❌ Unchecked cast without need
// ❌ Class.newInstance() (deprecated)
🎯 Interview Cheat Sheet
Must know:
new T()— compilation error: “cannot instantiate T”- Reason: type erasure — after compilation T -> Object, JVM doesn’t know T’s constructor
- Solution 1:
Class<T>—type.getDeclaredConstructor().newInstance() - Solution 2:
Supplier<T>—factory.get()— most flexible and fastest approach - Solution 3: Abstract factory method — subclass implements creation
Class.newInstance()deprecated in Java 9+, usegetDeclaredConstructor().newInstance()
Frequent follow-up questions:
- Why can’t you do new T()? — Type erasure: T is replaced with Object, no constructor info
- **Which approach is better — Class
or Supplier?** — Supplier is more flexible and faster (~1-5 ns vs ~100-200 ns) - Can you create T with parameters via Class? — Yes:
type.getConstructor(String.class).newInstance(name) - What is the TypeReference pattern? — Jackson style:
new TypeReference<User>() {}for getting generic type
Red flags (DO NOT say):
- ❌ “new T() works via unchecked cast” — This is a compilation error, not a warning
- ❌ “Class.newInstance() is the best way” — Deprecated in Java 9+, use getDeclaredConstructor
- ❌ “Supplier is slower than Class” — Supplier is faster (~1-5 ns vs ~100-200 ns)
- ❌ “You can create T via reflection without Class” — You need Class
or Supplier
Related topics:
- [[11. What are Generics in Java]]
- [[13. What is type erasure]]
- [[14. Can you create an array of generic type]]
- [[15. What are bounded type parameters]]