Question 21 Β· Section 20

What is the difference between List and List?

After type erasure, both types become just List. The difference is only in compile-time checks.

Language versions: English Russian Ukrainian

🟒 Junior Level

List<Object> β€” a concrete type: a list of objects.

  • You can add any objects (String, Integer, etc.).
  • Invariant: List<Object> is NOT a parent of List<String>.
List<Object> objects = new ArrayList<>();
objects.add("Hello");
objects.add(42);  // βœ… allowed

// But:
List<String> strings = new ArrayList<>();
// process(objects);  // βœ…
// process(strings);  // ❌ List<String> != List<Object>

List<?> β€” a list of unknown type.

  • You can read elements only as Object.
  • Cannot write (except null).
  • Covariant: accepts List<String>, List<Integer>, etc.
List<?> unknown = List.of("a", "b", "c");
Object o = unknown.get(0);  // βœ…
// unknown.add("x");  // ❌ cannot write

List<String> strings = new ArrayList<>();
process(strings);  // βœ… List<?> accepts any parameterized List

🟑 Middle Level

What happens at runtime

After type erasure, both types become just List. The difference is only in compile-time checks.

// List<Object> β€” invariant
void process1(List<Object> list) { }
process1(new ArrayList<String>());  // ❌ compilation error

// List<?> β€” covariant
void process2(List<?> list) { }
process2(new ArrayList<String>());  // βœ… OK

Why List<?> forbids writing

List<?> unknown = new ArrayList<String>();
// unknown.add(42);  // ❌

The compiler doesn’t know the actual element type. If writing Integer were allowed, but the list is actually a List<String> β€” there would be a ClassCastException on read.

Raw List vs List<?>

// ❌ Raw type β€” all checks disabled
List raw = new ArrayList();
raw.add("Hello");
raw.add(42);  // compiler is silent!

// βœ… Wildcard β€” checks in place
List<?> wildcard = new ArrayList<String>();
wildcard.add(42);  // ❌ compilation error

πŸ”΄ Senior Level

Reification and erasure

At the bytecode level, both options are simply List. List<?> is safer because the compiler blocks unsafe operations, whereas raw type simply disables checks.

List<?> does not give performance advantages

List<?> is not read-only by design. The compiler blocks writes because it cannot verify type safety, not because it treats the collection as immutable. No performance optimizations follow from this.

Capture of Wildcard

// Cannot do this:
void swap(List<?> list, int i, int j) {
    // var temp = list.get(i);
    // list.set(i, list.get(j));  // ❌ capture of ?
}

// Solution β€” capture helper:
void swap(List<?> list, int i, int j) {
    swapHelper(list, i, j);
}
private <T> void swapHelper(List<T> list, int i, int j) {
    T temp = list.get(i);
    list.set(i, list.get(j));
    list.set(j, temp);
}

Best Practices

// βœ… List<?> for arguments β€” maximum flexibility
void printAll(List<?> list) { }

// βœ… List<Object> only for heterogeneous containers
List<Object> mixed = new ArrayList<>();

// ❌ Raw List β€” disables all checks
// ❌ List<Object> when wildcard is needed

🎯 Interview Cheat Sheet

Must know:

  • List<Object> β€” invariant, concrete type, can add any objects
  • List<?> β€” covariant, unknown type, read only as Object, cannot write
  • List<String> is NOT a subtype of List<Object> β€” generics are invariant
  • List<?> accepts any parameterized List: List<String>, List<Integer>
  • List<?> != raw List β€” wildcard preserves checks, raw disables all
  • Capture of wildcard β€” cannot get/set for ? in one method, need helper <T>

Frequent follow-up questions:

  • Can you add null to List<?>? β€” Yes, null is valid for any type
  • **How does List<?> differ from List?** β€” List<?> is covariant, List is invariant
  • What is capture of wildcard? β€” The compiler cannot match the ? type for writing
  • **When to use List?** β€” Heterogeneous containers with different types

Red flags (DO NOT say):

  • ❌ β€œList<?> is a read-only list” β€” The compiler blocks writes due to unknown type, not immutable
  • ❌ β€œList accepts List" β€” Generics are invariant, List != List
  • ❌ β€œList<?> gives performance advantages” β€” No runtime optimizations
  • ❌ β€œList<?> and raw List are the same” β€” Wildcard preserves checks, raw disables them

Related topics:

  • [[11. What are Generics in Java]]
  • [[16. What is the difference between <? extends T> and <? super T>]]
  • [[17. What is PECS (Producer Extends Consumer Super)]]
  • [[19. What are raw types and why should you avoid them]]