The answers given here didn't fully convince me. So instead, I make another example.
public void passOn(Consumer<Animal> consumer, Supplier<Animal> supplier) {
consumer.accept(supplier.get());
}
sounds fine, doesn't it? But you can only pass Consumer
s and Supplier
s for Animal
s. If you have a Mammal
consumer, but a Duck
supplier, they should not fit although both are animals. In order to disallow this, additional restrictions have been added.
Instead of the above, we have to define relationships between the types we use.
E. g.,
public <A extends Animal> void passOn(Consumer<A> consumer, Supplier<? extends A> supplier) {
consumer.accept(supplier.get());
}
makes sure that we can only use a supplier which provides us the right type of object for the consumer.
OTOH, we could as well do
public <A extends Animal> void passOn(Consumer<? super A> consumer, Supplier<A> supplier) {
consumer.accept(supplier.get());
}
where we go the other way: we define the type of the Supplier
and restrict that it can be put into the Consumer
.
We even can do
public <A extends Animal> void passOn(Consumer<? super A> consumer, Supplier<? extends A> supplier) {
consumer.accept(supplier.get());
}
where, having the intuitive relations Life
-> Animal
-> Mammal
-> Dog
, Cat
etc., we could even put a Mammal
into a Life
consumer, but not a String
into a Life
consumer.