GotchaDifficulty: Intermediate1 min

Why a List<String> Is Not a List<Object>

A String is an Object, and still the compiler refuses to pass a list of strings as a list of objects.

Even though every String is an Object, a List<String> cannot be passed where a List<Object> is expected. Allowing it would let the receiving code add a non-string to a list that promised to hold only strings.

A method that takes a List<Object> refuses a List<String>, which reads like an inconsistency:

text
Report.java:8: error: incompatible types: List<String> cannot be converted to List<Object>

Imagine it were allowed. The method would believe it held a list of objects, and nothing would stop this:

java
void spoil(List<Object> anything) {
    anything.add(42);
}

List<String> refs = new ArrayList<>();
spoil(refs);

String first = refs.get(0);

Line 2 is legal for a list of objects. Line 8 would then fail with a ClassCastException on a list whose declaration promised it held only strings, which is exactly the failure generics exist to remove.

The rule has a name, invariance: a generic type with one type argument is unrelated to the same type with another, whatever the relationship between the two arguments.

When a method only reads, say so with a wildcard, a ? that stands for some type you do not name: List<? extends Object>, usually written List<?>. That accepts a list of anything and refuses to add to it, which is the combination that makes it safe.