Issue
In netty define the listener as below: for example in the class io.netty.util.concurrent.CompleteFuture:
@Override
public Future<V> addListener(GenericFutureListener<? extends Future<? super V>> listener) {
if (listener == null) {
throw new NullPointerException("listener");
}
DefaultPromise.notifyListener(executor(), this, listener);
return this;
}
From what I've known:Future<? super V> means a collections of Class which supers V. So it is a collections as in the inheritance tree, for we have Future<V> and Future<Object> at least. Let's say the collections' name is C.
So here comes the question, what does ? extends C means while C is a collection?
Hope somebody could enlighten me!
Solution
I'm assuming your declaration above comes from Netty's Promise class. I think my answer should work irrespective of that because your question seems to be about covariance and contravariance more than about Netty's API explicitly.
public interface Promise<V> extends Future<V> {
Promise<V> addListener(GenericFutureListener<? extends Future<? super V>> listener);
//...
}
The intention of generics here is to make the API more malleable for its users.
Suppose you have three different GenericFutureListener
objects of three different type arguments: Integer
, Number
and Object
.
GenericFutureListener<Future<Integer>> fl1 = System.out::println;
GenericFutureListener<Future<Number>> fl2 = System.out::println;
GenericFutureListener<Future<Object>> fl3 = System.out::println;
Notice that Integer
is a subtype of Number
which in turn is a subtype of Object
.
Suppose now that we have a Promise
of type Integer
, somewhat as follows
Promise<Integer> p = somePromise;
Our method declaration would be interpreted by the compiler as
Promise<Integer> addListener(GenericFutureListener<? extendsFuture<? super Integer>> listener);
Which basically is saying that the GenericFutureListener
might operate on futures of type Integer
, or any of its super types.
This clearly makes the API much more flexible, for example, I can add any of the listeners I defined before to be notified when my promise of Integer is resolved:
p.addListener(fl1);
p.addListener(fl2);
p.addListener(fl3);
Notice that I was not forced to provide a listener for a future of explicitly type Integer
. If you think about it that totally makes sense because if my promise p
produces an Integer
, and an Integer
is a Number
, then a listener that knows how to handle a future of Number
should be able to handle a future of Integer
as well. And if I have listener that knows how to handle a future of Object
, and an Integer
is an Object
, then there should be no problem in letting a listener for a future of Object
to handle a future of Integer
, right?
Well, that's exactly what Future<? super V>
means in the declaration above. This is concept known as contravariance.
Now, the truth is that in Netty Future
is an interface, and many different classes may implement Future
. We would like our GenericFutureListener
to be able to use any subtype of Future
and not just Future
itself, right?.
For example, a Promise
is in fact a subtype of Future
:
GenericFutureListener<Promise<Integer>> fl4 = System.out::println;
GenericFutureListener<Promise<Number>> fl5 = System.out::println;
GenericFutureListener<Promise<Object>> fl6 = System.out::println;
And as you can see, GenericFutureListener
accepts Promise
as a type argument here. This is so thanks to the declaration of <? extends Future>
. Without it, GenericFutureListener
would only accept the Future
type here and that would make this API much less flexible, right?.
This concept is called covariance and once again it is used to make the API much more flexible for its users.
Now we can make our original promise also add these second set of listeners:
p.addListener(fl4);
p.addListener(fl5);
p.addListener(fl6);
And there you have it. A much more flexible API thanks to the proper use of covariance and contravariance.
Answered By - Edwin Dalorzo
Answer Checked By - Mildred Charles (JavaFixing Admin)