🧪 Generics · Intermediate

Raw types in Java

Legacy compatibility, unchecked warnings, heap pollution.

🧩 The mysteryYour List<String> contains an Integer. No warning you noticed, no crash where it went in. It explodes later, somewhere innocent. How did it get there?

Generics with the label ripped off

A raw type is a generic type used without type arguments: List instead of List<String>. They exist only for compatibility with code written before Java 5. Through a raw type, the compiler stops checking and just emits unchecked warnings.

List raw = new ArrayList<String>();
raw.add(1);  // compiles: unchecked warning

Raw ≠ List<Object> ≠ List<?>

Three very different things. **List<Object>: checked, holds any Object. List<?>: checked, some unknown type, read-only-ish. List (raw): no checking at all**.

List<Object> a; // checked, accepts Objects
List<?> b;      // checked, unknown type
List c;         // unchecked: anything goes
🔮 Predict it

Your turn

What happens when this runs?

List<String> strs = new ArrayList<>();
List raw = strs;
raw.add(7);
System.out.println(strs.size());
String s = strs.get(0);
  1. Compile error at raw.add(7)
  2. Prints 1, then throws ClassCastException
  3. Prints 1 and ends normally
Show the answer

The raw alias lets an Integer into a List<String>, and nothing complains (size prints 1). The crash comes at strs.get(0), where the compiler-inserted cast to String fails.

Heap pollution

That's heap pollution: a variable of a generic type pointing at an object holding the wrong type. Erasure means the list itself never checks, so the bomb only goes off when someone reads the element, often far from the bad write.

⚠️ The trap

Raw erases EVERYTHING

Use a class raw and all generics of its instance members are erased, even ones unrelated to T. Here names() returns a raw List, so get(0) gives Object and the assignment doesn't compile.

class Holder<T> {
    List<String> names() {
        return List.of("a");
    }
}
Holder raw = new Holder();
String s = raw.names().get(0); // ✗ Object

When you don't know the type

✗ Raw
static void dump(List items) {
    items.add("oops"); // allowed!
}

No checks at all: anything can be added to any list passed in.

✓ Wildcard
static void dump(List<?> items) {
    for (Object o : items) IO.println(o);
}

Still accepts every list, but the compiler blocks unsafe adds.

💼 In the real world

In legacy code

Old libraries and pre-2004 code are full of raw types. Compile with -Xlint:unchecked to see every risky spot. If you must silence a warning, put @SuppressWarnings("unchecked") on the smallest possible scope, ideally one variable, after convincing yourself it's safe.

Key takeaways

  1. List (raw) ≠ List<Object> ≠ List<?>
  2. Raw types silence type checks and give unchecked warnings
  3. Heap pollution: wrong-typed element hidden inside a typed list
  4. Using a raw type erases generics of ALL its instance members
🤯 Did you know?

The term "heap pollution" comes straight from the Java Language Specification (section 4.12.2), which describes exactly this: a variable of a parameterized type referring to an object that isn't of that type.

Practice questions

Why does Java still allow raw types?

  1. They are faster than generic types
  2. For compatibility with code written before generics existed (Java 5)
  3. They are needed to store primitives
  4. They're required for arrays
Check your answer

For compatibility with code written before generics existed (Java 5). Generics were added in Java 5 using erasure precisely so old, non-generic code could keep compiling and interoperating.

Does this compile?

List raw = new ArrayList<String>();
raw.add(1);
  1. No — type mismatch on add
  2. Yes, with an unchecked warning
  3. Yes, and add(1) throws at runtime
  4. No — raw types were removed in Java 9
Check your answer

Yes, with an unchecked warning. Through a raw reference the compiler can't check the element type, so it just warns about an unchecked call. At runtime the Integer goes in without complaint.

Final generics boss: <T extends Comparable<? super T>>. It looks like line noise. By the end of the next lesson it'll make perfect sense.