It is a question of covariance and contravariance of generic types.
Function types are contravariant in their parameters. If B
is a subclass of A
, then Consumer<A>
could be considered a subclass of Consumer<B>
: whenever you need a Consumer<B>
you can use a Consumer<A>
, because it accepts A
as a parameter, so it accepts also B
.
On the other hand functions are covariant in their return type: a Supplier<B>
have a return value which can be cast to A
, so you could consider it a subclass of Supplier<A>
.
Unfortunately such definitions do not work well with mutable structures: if you allow List<B>
to be a subclass of List<A>
then you can write:
final List<B> listB = new ArrayList<>();
final List<A> listA = listB;
listA.add(new A()); // it is no longer a List<B>
final B b = listB.get(0); // ClassCastException
Therefore Java chose invariant generics: Consumer<B>
is neither a subclass nor a superclass of Consumer<A>
. But we want to remember that Consumer<B>
is de facto a subclass of Consumer<A>
so we use bounds in the signatures of functions.
Of course one might write:
public <U super T> void ifPresent(Consumer<U super T> action) { ... }
(the T
parameter refers to the type parameter of Optional<T>
), but U
is never used in the signature. So we use the wildcard ? super T
.
Other languages like Scala allow to specify if it is safe to use a generic type in a covariant or contravariant way in the class definition. So Scala whould define:
def ifPresent(Consumer[T] action): Unit { ... }
since the compiler already knows, that the parameter T
in Consumer
is contravariant.
Remark: if you don't write to a List
, you can also use it in a covariant way through the type List<? extends A>
. The compiler will not allow you to call add(new A())
on such an object, since ?
can also be a subclass of A
, in which case the call would break the List
. But a call to get()
will certainly return something assignable to A
.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…