This goes back to what it means for A
to be a subtype of B
. The formal name for this is the Liskov substitution principle, which says basically that A
is a subtype of B
if and only if you can take any valid program that has in it something of type B
, swap in something of type A
and it would still be a valid program. The effect of this is that if you can use an A
wherever you could use a B
, then A
is a subtype of B
.
So in this case, since this is part of a valid (valid meaning "compiling") program:
public static void doThing(List<Object> x) {
x.add(new Object());
}
Then, by the Liskov substitution principle, if List<String>
were a subtype of List<Object>
this would be part of a valid program too:
public static void doThing(List<String> y) {
y.add(new Object());
}
But clearly that second snippet can't compile. Therefore, that second snippet is not part of a valid program, and therefore List<String>
is not a subtype of List<Object>
.
Likewise, the other way around is also not true: List<Object>
is not a subtype of List<String>
. Finding the program snippet to prove that is left as an exercise for the reader.