What is a raw type in Java, why should you avoid raw types, and what do you lose by using them?
answer
- Raw = generic with no <type> filled in
- Exists only for pre-Java-5 backward compatibility
- Turns OFF compile-time checking -> ClassCastException at runtime
- List<?> is the SAFE 'I don't care' alternative
- Triggers unchecked warnings
basics
~20 sA raw type is a generic class used without its type parameter, like List instead of List<String>. Avoid them because you lose compile-time type checking, so wrong types slip in and blow up at runtime with a ClassCastException.
solid answer
~40 sA raw type is a generic type used without supplying its type argument, e.g. `List` instead of `List<String>`. Raw types exist only for backward compatibility with pre-Java-5 code. Using one opts out of generics: the compiler stops checking element types, so you can put any object into the collection and the error only surfaces as a ClassCastException when you read and cast it out. You also get unchecked warnings. Prefer a parameterized type for full safety, or `List<?>` (an unbounded wildcard) when you genuinely don't care about the element type but still want the compiler to forbid inserting anything except null. The rule: never use raw types in new code.
code
java · 11 lines// Raw type: compiles, then explodes at runtime
List raw = new ArrayList();
raw.add("hi");
raw.add(42); // no complaint from the compiler
String s = (String) raw.get(1); // ClassCastException
// Parameterized: the bug is caught at compile time
List<String> safe = new ArrayList<>();
safe.add("hi");
// safe.add(42); // COMPILE ERROR — fixed at the source
String t = safe.get(0); // no cast neededgo deeper
Recognize that List is raw and List<String> is parameterized, and that the raw one loses compile-time checking leading to a runtime ClassCastException.
Explain raw types exist for backward compatibility, articulate the runtime-vs-compile-time error shift, and distinguish raw vs List<Object> vs List<?>.
Reason about when raw types are still unavoidable (class literals, instanceof) due to erasure, and choose the unbounded wildcard as the safe substitute in APIs.
Discuss the language-design trade-off of retrofitting generics via erasure, the migration-compatibility motivation, and how raw-type usage erodes the team's type-safety guarantees at scale.
## What "generics" are A **generic type** is a class or interface that takes one or more **type parameters** — placeholders for a type you fill in later. `List<E>` is the generic interface; `E` is its type parameter. When you write `List<String>`, you supply the **type argument** `String`, producing a **parameterized type**. The benefit is **compile-time type safety**: the compiler knows the list holds only `String`s, rejects anything else, and inserts the casts for you so you never write them. ## What a raw type is A **raw type** is the name of a generic type used with *no* type argument at all: just `List`, `Map`, `Comparable`. Raw types exist for one reason — **backward compatibility**. Generics arrived in Java 5 (2004); huge amounts of code already used `List`. To let old and new code interoperate, the language kept raw types legal. ## What you lose Using a raw type **silently turns off generics for that variable**. Consider: ```java List raw = new ArrayList(); // raw type raw.add("hello"); raw.add(42); // compiler allows it! String s = (String) raw.get(1); // ClassCastException at RUNTIME ``` With a raw type the compiler no longer tracks element types, so `add(42)` compiles. The mistake stays hidden until that line runs, far from where the bug was introduced. With `List<String>`, `raw.add(42)` would be a **compile error** — caught immediately, at the source. The whole point of generics is to move errors from runtime to compile time, and raw types throw that away. You also get **"unchecked" warnings** from the compiler whenever you mix raw and parameterized code — its way of saying "I can't guarantee type safety here." ## Raw type vs `List<Object>` vs `List<?>` These are three different things, often confused: - **`List` (raw)** — no checking; unsafe; legal only for compatibility. - **`List<Object>`** — explicitly "a list that holds any object." Still type-checked: you cannot pass a `List<String>` where a `List<Object>` is expected (they aren't subtypes — generics are *invariant*). - **`List<?>` (unbounded wildcard)** — "a list of some unknown type." Type-safe: you can read elements as `Object`, but the compiler forbids `add`ing anything except `null`, because it can't know the actual element type. This is the safe replacement when you truly don't care about the type parameter. ## The takeaway Never use raw types in new code. Reach for a parameterized type to get full checking, or an unbounded wildcard (`<?>`) when the element type is irrelevant but you still want safety. The only place you'll legitimately still see raw types is class literals (`List.class`, not `List<String>.class`, which is illegal) and `instanceof` (`o instanceof List`), because generics are erased at runtime.
- If raw types are unsafe, why didn't Java just remove them?Backward compatibility. Generics were retrofitted in Java 5 onto a language with millions of lines of existing collection code using `List`, `Map`, etc. Removing raw types would have broken all of it, so they stay legal (with warnings) to let old and new code interoperate.
- What's the difference between List<Object> and List<?>?`List<Object>` is a concrete parameterized type holding any object; you can add any object to it. `List<?>` is a list of *unknown* type; you can read elements as Object but cannot add anything except null, because the compiler can't verify what the real element type is.
A raw type is like a labelled storage box where you peeled the label off. The box still works, but nothing stops you from dumping mismatched junk in; you only discover the mistake when you reach in expecting one thing and pull out another.
saying these in an interview costs you the question
- Saying raw types and List<Object> are the same thing — they're not; List<Object> is still type-checked.
- Claiming a raw type causes a compile error — it compiles fine (with a warning); the failure is at runtime.
- Thinking you can freely add elements to a List<?> — you can only add null.
- Believing generics survive at runtime so a raw type 'remembers' its element type — erasure removes it.