IMO This is probably the most complex concept in vanilla Java. So let's break this down a bit. I'll start with your second question.
Function<T, R>
takes an instance t
of type T
and returns an instance r
of type R
. With inheritance that means that you could supply an instance foo
of type Foo
if Foo extends T
and similarly return bar
of type Bar
if Bar extends R
.
As a library maintainer who wants to write a flexible generic method, it's hard, and actually impossible, to know in advance all the classes which might be used with this method which extend T
and R
. So how are we going to write a method that handles them? Further, the fact that these instances have types which extend the base class is none of our concern.
This is where the wildcard comes in. During the method call we say that you can use any class which meets the envelope of the required class. For the method in question, we have two different wildcards using upper and lower bounded generic type parameters:
public interface Function<T, R>{
default <V> Function<V, R> compose(Function<? super V, ? extends T> before)
Lets now say that we want to take advantage of this method... for the example lets define some basic classes:
class Animal{}
class Dog extends Animal{}
class Fruit{}
class Apple extends Fruit{}
class Fish{}
class Tuna extends Fish{}
Imagine our function and transformation is defined as below:
Function<Animal, Apple> base = ...;
Function<Fish, Animal> transformation = ...;
We can combine these functions using compose
to create a new function:
Function<Fish, Apple> composed = base.compose(transformation);
This is all fine and dandy, but now imagine that in the desired output function we actually only want to use Tuna
as the input. If we did not use the lower-bounded ? super V
as the input type parameter for the Function
we pass to compose
then we would get a compiler error:
default <V> Function<V, R> compose(Function<V, ? extends T> before)
...
Function<Tuna, Apple> composed = base.compose(transformation);
> Incompatible types:
> Found: Function<Fish, Apple>, required: Function<Tuna, Apple>
This happens because the return type for the call to compose
specifies V
as Tuna
while transformation
on the other hand specifies its "V
" as Fish
. So now when we try to pass transformation
to compose
the compiler requires transformation
to accept a Tuna
as its V
and of course Tuna
does not identically match Fish
.
On the other hand, the original version of the code (? super V
) allows us to treat V
as a lower bound (i.e. it allows "contravariance" vs. "invariance" over V
). Instead of encountering a mismatch between Tuna
and Fish
the compiler is able to successfully apply the lower bound check ? super V
which evaluates to Fish super Tuna
, which is true since Tuna extends Fish
.
For the other case, imagine our call is defined as:
Function<Animal, Apple> base = ...;
Function<Fish, Dog> transformation = ...;
Function<Fish, Apple> composed = base.compose(transformation);
If we did not have the wildcard ? extends T
then we would get another error:
default <V> Function<V, R> compose(Function<? super V, T> before)
Function<Fish, Apple> composed = base.compose(transformation);
// error converting transformation from
// Function<Fish, Dog> to Function<Fish, Animal>
The wildcard ? extends T
allows this to work as T
is resolved to Animal
and the wildcard resolves to Dog
, which can satisfy the constraint Dog extends Animal
.
For your first question; these bounds really only work in the context of a method call. During the course of the method, the wildcard will be resolved to an actual type, just as ? super V
was resolved to Fish
and ? extends T
was resolved to Dog
. Without the information from the generic signature, we would have no way for the compiler to know what class can be used on the type's methods, and therefore none are allowed.