I have encountered this class in code that I'm maintaining:
new GenericFutureListener<Future<? super Void>>() {...}
I'm having a really hard time understanding what this means. A Future
containing a type that is either Void
or its superclass - Object
. So why not just write Future<Object>
?
super T denotes an unknown type that is a supertype of T (or T itself; remember that the supertype relation is reflexive). It is the dual of the bounded wildcards we've been using, where we use ? extends T to denote an unknown type that is a subtype of T .
Implementing generics into your code can greatly improve its overall quality by preventing unprecedented runtime errors involving data types and typecasting.
Something nobody else pointed out: it's Netty and if you look at https://netty.io/4.0/api/io/netty/util/concurrent/class-use/Future.html you'll see there are some methods taking just this type in the library, e.g. ChannelPromise.addListener(GenericFutureListener<? extends Future<? super java.lang.Void>> listener)
. And these methods have this signature because of the generic case: Future.addListener(GenericFutureListener<? extends Future<? super V>> listener)
. ChannelPromise
merely extends Future<Void>
.
The generic case makes sense because 1. if you have a FutureListener<Future<Object>>
, it can handle any Object
value when a future completes; 2. since a V
is an Object
it can handle V
. Thus it can listen to a Future<V>
. Obviously the same holds for any supertype of V
instead of Object
.
Of course, these signatures would be much simpler if Java had declaration-site variance, but it doesn't and probably won't any time soon.
It looks like this is being created to conform to some generic type like GenericFutureListener<Future<? super T>>
, which is not flexible enough to allow for GenericFutureListener<Future<Void>>
alone.
For instance if you have something like this:
class GenericFutureListener<T> {}
public static <T> void m(GenericFutureListener<Future<? super T>> p) {
...
}
m(new GenericFutureListener<Future<? super Void>>() {}); // works,
m(new GenericFutureListener<Future<Void>>() {}); // does not work
Passing in just a GenericFutureListener<Future<Void>>
is not allowed.
As you've noted, this Future<? super Void>
can only be a void future, or Object
future.
But:
So why not just write Future?
The intention of the developer was almost certainly to limit "listeners" to void actions. But if one allowed Future<Object>
, then any other type could be used as the Future
's value, because a Future<Object>
could hold a String
(or any other type) result.
java.lang.Void
being a final
class, it doesn't allow any child classes, which effectively almost completely limits it to void actions (unless one has practical value in calling new Object()
when setting the result of the future)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With