skip to content

Is List<?> just a fancy way of writing the raw type List? Explain the practical difference.

level: juniorimportance: should knowfreq 40%

answer

  1. Raw List = type checking OFF (legacy)
  2. List<?> = type checking ON
  3. Raw: add anything + unchecked warning
  4. Wildcard: add only null, read as Object
  5. Never use raw types in new code

basics

~20 s

No. The raw List turns off generic checking and lets you do unsafe things with warnings. List<?> keeps the type checks on: you read as Object and can only add null, so the compiler still protects you.

solid answer

~40 s

They are very different despite both meaning 'a list of unspecified type'. The raw type List is the pre-generics form kept for backward compatibility; using it disables generic type checking for that reference, so you can call add with any object and only get an unchecked warning - safety is off. List<?> is a real parameterized type that keeps type checking fully on: the compiler treats the element type as a single unknown type, so it lets you read elements as Object but refuses every add except null. In short, raw List trades safety for permissiveness, while List<?> keeps safety by being read-only. You should never use raw types in new code; if you want 'a list of anything', use List<?>.

go deeper

for a junior

Knows List<?> is type-safe and the raw type is not, and that raw types should be avoided.

for a middle

Explains that the raw type disables checking (unchecked warnings, runtime ClassCastException) while the wildcard keeps it.

for a senior

Frames the difference historically and in terms of soundness, and can articulate exactly which operations differ.

for a principal

Establishes a 'no raw types' policy and explains how the wildcard preserves the type-safety guarantees the raw type sacrifices.

## Both say 'list of unspecified type' - but for opposite reasons When people first see `List<?>` they often assume it is just a verbose way to write the old `List`. It is not. The two come from different eras of Java and behave oppositely. ## The raw type `List` Before Java 5 (2004) there were no generics, so you just wrote `List`. When generics arrived, the old syntax was kept for **backward compatibility** - this is the **raw type**. Using a raw type does something drastic: it **turns off generic type checking** for that reference. You can put anything in: ``` List raw = new ArrayList(); raw.add("hello"); raw.add(42); // compiles - no error, only an 'unchecked' warning String s = (String) raw.get(1); // ClassCastException at runtime! ``` The compiler warns ('unchecked call') but does not stop you. You have **lost type safety**, and bugs surface at runtime as `ClassCastException`. ## The wildcard type `List<?>` `List<?>` is a genuine parameterized type. The element type is **unknown but checked**. The compiler keeps all safety guarantees: ``` List<?> any = new ArrayList<String>(); any.add("hello"); // COMPILE ERROR - cannot prove the unknown type is String any.add(null); // OK Object o = any.get(0); // OK, read as Object ``` No unsafe operation slips through. The price is that you can't add (except `null`) and can only read as `Object` - but that's exactly the safety the raw type threw away. ## Summary of the difference | | raw `List` | `List<?>` | |---|---|---| | Era | pre-generics, compatibility only | modern generics | | Type checking | **off** (unchecked warnings) | **on** | | `add(x)` | allowed (unsafe) | rejected except `null` | | `get()` | returns `Object`, unchecked | returns unknown type, read as `Object` | | Recommended? | **never** in new code | yes, for 'a list of anything' | ## The takeaway Use `List<?>` when you mean 'a list whose element type I don't care about but want to handle safely'. Reserve the raw type for nothing - avoid it entirely. The compiler-enforced restriction on `List<?>` is a *feature*: it stops you from doing the unsafe writes the raw type would silently allow.

  • Why does the raw type still exist if it is unsafe?
    For backward compatibility with pre-generics (Java 4 and earlier) code, so old libraries and codebases keep compiling. It is retained, not recommended.
  • Which one triggers 'unchecked' compiler warnings?
    The raw type. Operations through a raw reference (like add) are unchecked. List<?> is fully checked and produces no such warnings.

saying these in an interview costs you the question

  • Saying they are equivalent - they have opposite safety behavior
  • Believing List<?> emits unchecked warnings like the raw type does
  • Using raw types in new code 'because it's shorter'
  • Thinking the add restriction on List<?> is a bug rather than a safety feature

context