What are raw types and why should you avoid them
A raw type is a generic class without type arguments:
🟢 Junior Level
Raw type — using a generic class without specifying a type.
// ✅ Parameterized type
List<String> list = new ArrayList<>();
list.add("Hello");
String s = list.get(0); // no cast needed
// ❌ Raw type — no type
List list = new ArrayList(); // raw type!
list.add("Hello");
list.add(42); // compiler doesn't complain, but this is a bug!
String s = (String) list.get(0); // cast required
String s2 = (String) list.get(1); // ClassCastException!
Why to avoid:
- ❌ No compile-time type checking
- ❌ Casts required
- ❌ Risk of
ClassCastExceptionat runtime
🟡 Middle Level
What is a raw type
A raw type is a generic class without type arguments:
// Generic class
public class Box<T> {
private T value;
public void set(T value) { this.value = value; }
public T get() { return value; }
}
// Parameterized usage
Box<String> stringBox = new Box<>(); // ✅
// Raw type
Box rawBox = new Box(); // ❌ warning: raw type
What happens:
Box rawBox = new Box();
rawBox.set("Hello");
rawBox.set(42); // compiler doesn't complain!
// After type erasure:
Box rawBox = new Box();
rawBox.set("Hello");
rawBox.set(Integer.valueOf(42)); // everything is stored
// On read — ClassCastException
String s = (String) rawBox.get(); // OK
Integer i = (Integer) rawBox.get(); // OK
String s2 = (String) rawBox.get(); // ClassCastException!
Common mistakes
- Legacy code compatibility: ```java // Pre-Java 5 code List list = new ArrayList(); list.add(“Hello”);
// New code
List
2. **Mixing raw and parameterized:**
```java
Map<String, List<String>> map = new HashMap<>();
Map rawMap = map; // raw type — type safety lost
rawMap.put("key", 42); // compiler doesn't complain
// Later — ClassCastException
List<String> list = map.get("key"); // ClassCastException!
Practical usage
The only acceptable raw type usage — getClass():
// getClass() returns Class<? extends |X|>, not raw type.
// Assigning to Class — unchecked conversion, not intended design.
Class<?> clazz = "Hello".getClass();
Class rawClass = "Hello".getClass(); // also OK, but unchecked conversion
// But wildcard is better
Class<?> better = "Hello".getClass();
When raw types are acceptable
- Compatibility with pre-Java 5 libraries
- Certain reflection scenarios
- Class literals:
Class.class(thoughClass<?>is better)
🔴 Senior Level
Internal Implementation
Type erasure with raw types:
// Raw type — all type checks disabled
List<String> typed = new ArrayList<>();
List raw = typed;
raw.add(42); // compiler allows!
raw.add(true); // compiler allows!
// But type erasure stores data as Object
// raw = typed after erasure — the same object
Unchecked warnings:
// Compiler gives warnings:
// warning: [unchecked] unchecked call to add(E) as a member of raw type List
raw.add(42);
// warning: [unchecked] unchecked conversion
List<String> list = raw;
// warning: [rawtypes] raw type
Box box = new Box();
Architectural Trade-offs
Raw types vs parameterized:
| Raw types | Parameterized |
|---|---|
| No type checking | Compile-time check |
| Risk of ClassCastException | Type-safe |
| Legacy compatibility | Modern code |
| No cast on access | No cast needed |
Edge Cases
1. Raw type in inheritance:
public class Node<T> {
private T data;
public T getData() { return data; }
}
public class RawNode extends Node { // raw type!
@Override
public Object getData() { return super.getData(); }
}
// After erasure — bridge methods work correctly
// But type safety is lost
2. Raw type and generic methods:
public class Util {
public static <T> T identity(T t) { return t; }
}
// Call via raw type
Util u = new Util(); // raw type
Object result = u.identity("Hello"); // T inferred as Object
// Correct
Object result2 = Util.identity("Hello"); // T = String
3. Raw type in arrays:
// Arrays of generic classes — raw type
Box<String>[] boxes = new Box[10]; // warning: raw type
// Better
List<Box<String>> boxes = new ArrayList<>();
Performance
Raw types vs parameterized:
- Runtime: the same (type erasure)
- Compile time: raw types faster (no checking)
- Safety: parameterized significantly safer
Performance is the same, but raw types are risky
Production Experience
Legacy migration:
// Before Java 5
public class OldDao {
public List findAll() {
return jdbcTemplate.query("SELECT * FROM users", ...);
}
}
// After migration
public class NewDao {
public List<User> findAll() {
return jdbcTemplate.query("SELECT * FROM users", ...);
}
}
// Transitional period — raw type warnings
List users = oldDao.findAll(); // raw type
List<User> typedUsers = newDao.findAll(); // parameterized
@SuppressWarnings(“rawtypes”):
// Only when unavoidable
@SuppressWarnings("rawtypes")
public void legacyInterop() {
List rawList = legacyMethod();
// ...
}
// ❌ Don't suppress warnings without reason
// ✅ Fix the root cause
Best Practices
// ✅ Always use type arguments
List<String> list = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();
// ✅ Diamond operator (Java 7+)
List<String> list = new ArrayList<>(); // not new ArrayList<String>()
// ✅ Wildcards when type is unknown
public void process(List<?> list) { }
// ⚠️ Raw types only for legacy code
// ⚠️ Suppress warnings only when unavoidable
// ❌ Raw types in new code
// ❌ Mixing raw and parameterized
// ❌ Ignoring unchecked warnings
🎯 Interview Cheat Sheet
Must know:
- Raw type — using a generic class without a type argument:
List list = new ArrayList() - Raw types disable compile-time type checking — risk of ClassCastException at runtime
- Mixing raw and parameterized types infects the entire chain with unchecked warnings
- The only acceptable raw type — legacy code and certain reflection scenarios
- After type erasure, raw and parameterized — the same bytecode
@SuppressWarnings("rawtypes")— only when unavoidable, not for suppressing without reason
Frequent follow-up questions:
- When are raw types acceptable? — Pre-Java 5 legacy libraries, some reflection scenarios
- What happens when mixing raw and parameterized? — Unchecked warnings, loss of type safety
- Raw type vs wildcard <?>? — Raw disables all checks, wildcard preserves checks
- Can you create an array of generic classes? — Yes, but it will be a raw type:
new Box[10]
Red flags (DO NOT say):
- ❌ “Raw types are faster than parameterized” — Runtime performance is the same
- ❌ “Raw types are normal practice in new code” — Always use type arguments
- ❌ “Raw type is the same as <?>” — Raw disables checks, wildcard preserves them
- ❌ “SuppressWarnings solves the problem” — The warning remains, you need to fix the root cause
Related topics:
- [[11. What are Generics in Java]]
- [[12. What are the advantages of using Generics]]
- [[13. What is type erasure]]
- [[21. What is the difference between List<?> and List