Why this code does not compile (Parent
is an interface)?
List<? extends Parent> list = ...
Parent p = factory.get(); // returns concrete implementation
list.set(0, p); // fails here: set(int, ? extends Parent) cannot be applied to (int, Parent)
Here's my understanding.
Suppose we have a generic type with 2 methods
Suppose we have a super type
P
, and it has sub typesC1, C2 ... Cn
. (for convenience we sayP
is a subtype of itself, and is actually one of theCi
)Now we also got n concrete types
L<C1>, L<C2> ... L<Cn>
, as if we have manually written n types:We didn't have to manually write them, that's the point. There are no relations among these types
For C++ template, that's the end of story. It's basically macro expansion - based on one "template" class, it generates many concrete classes, with no type relations among them.
For Java, there's more. We also got a type
L<? extends P>
, it is a super type of anyL<Ci>
What kind of method should exist in
L<? extends P>
? As a super type, any of its methods must be hornored by its subtypes. This method would work:because in any of its subtype
L<Ci>
, there's a methodCi get()
, which is compatible withP get()
- the overriding method has the same signature and covariant return type.This can't work for
set()
though - we cannot find a typeX
, so thatvoid set(X)
can be overridden byvoid set(Ci)
for anyCi
. Thereforeset()
method doesn't exist inL<? extends P>
.Also there's a
L<? super P>
which goes the other way. It hasset(P)
, but noget()
. IfSi
is a super type ofP
,L<? super P>
is a super type ofL<Si>
.set(Si)
"overrides"set(P)
not in the usual sense, but compiler can see that any valid invocation onset(P)
is a valid invocation onset(Si)
It's doing that for the sake of safety. Imagine if it worked:
The meaning of
List<? extends Parent>
is "The is a list of some type which extendsParent
. We don't know which type - it could be aList<Parent>
, aList<Child>
, or aList<GrandChild>
." That makes it safe to fetch any items out of theList<T>
API and convert fromT
toParent
, but it's not safe to call in to theList<T>
API converting fromParent
toT
... because that conversion may be invalid.PECS - "Producer - Extends, Consumer - Super". Your
List
is a consumer ofParent
objects.