Interview Prep Zoneby CuriouserLabs

Question 3 of 5 · staff level

Why are Java arrays covariant but generics invariant? What is heap pollution?

🎤 Say this first

Arrays are covariant (Integer[] is-a Number[]) — a pre-generics decision that made polymorphic methods like Arrays.sort(Object[]) possible, but it moves type errors to runtime (ArrayStoreException). Generics are invariant (List<Integer> is not a List<Number>) so the compiler can reject the same mistake at compile time; wildcards reintroduce variance safely. Heap pollution is when a variable of parameterized type refers to an object that isn't of that type — typically via raw types or generic varargs — detonating later as a ClassCastException far from the cause.

The full picture

Same bug, two eras
// Arrays: compiles, blows up at RUNTIME
Object[] objs = new Integer[2];   // covariance allows this
objs[0] = "boom";                 // ArrayStoreException

// Generics: caught at COMPILE time
List<Integer> ints = new ArrayList<>();
// List<Object> objs2 = ints;     // does not compile — invariance

// Heap pollution via raw type:
List raw = ints;                  // raw type sidesteps checking
raw.add("boom");                  // warning only…
Integer i = ints.get(0);          // ClassCastException HERE, later, elsewhere
  • Arrays carry their component type at runtime and check every store — that's the safety net generics deliberately moved to compile time.
  • Generic varargs create arrays of erased type — hence the @SafeVarargs annotation on methods that promise not to store into or leak that array.
  • This is why 'prefer lists to arrays' (Effective Java Item 28) for API design — arrays + generics mix badly (new List<String>[10] is illegal).

🔄 Likely follow-up questions

  • Why is List<String>[] forbidden but List<?>[] allowed?
  • Explain what @SafeVarargs asserts and who is allowed to use it.
  • How would reified generics change this trade-off?