Reputation: 11882
A library I am contributing to maintains a registry of Class
es and handlers that can accept instances of these classes, think Consumer<T>
. There is a facility for registering a handler for a type *and all its subtypes`, e.g.
<T> void registerHandler(Class<T> type, Consumer<? super T>) { ... }
In addition, there is a way to receive a corresponding handler for a given type:
<T> Consumer<T> getHandler(Class<? extends T> type)
Now I can register a handler like this:
Consumer<Object> printer = System.out::println;
registerHandler(Object.class, printer);
Consumer<String> screamer = s -> System.out.println(s.toUpperCase());
registerHandler(String.class, screamer);
Retrieving the handler also works for subtypes:
getHandler(Object.class) // => printer
getHandler(String.class) // => screamer
getHandler(Integer.class) // => printer, because Integer is a subtype of Object
My question is about the getHandler
method, specifically its use of wildcard types. I see two other valid ways to use them:
<T> Consumer<T> getHandler(Class<? extends T> type) // option 1, from above
<T> Consumer<? super T> getHandler(Class<T> type) // option 2
<T> Consumer<? super T> getHandler(Class<? extends T> type) // option 3
// <T> Consumer<T> getHandler(Class<T> type) // non-option, does not work for subtypes.
Which is the best way to represent this relation between types?
An advantage I have found for using Class<? extends T>
is that the following works:
void handle(T obj, Class<? extends T> type) {
Consumer<? super Object> handler = getHandler(type);
// or Consumer<Object>, depending on whether ? super is in the return type or not
handler.accept(obj);
}
Object o = ...;
handle(o, o.getClass());
Without ? extends T
, the call to getHandler
within handle
would be invalid, unless it also uses Class<T>
, in which case the call with getClass
would be invalid.
Upvotes: 0
Views: 49
Reputation: 140318
<T> Consumer<T> getHandler(Class<? extends T> type)
This is the best option, because there is no wildcard in the return type.
Wildcards in return types are annoying, because you can't "get rid" of them. If you assign the return value to a variable, that variable has to have the wildcard in its declaration also.
There is something about this in Effective Java (don't have it to hand to point to the specific item), which says something along the lines of "if you have a wildcard in the return type, there is probably something wrong with your design".
(Of course, you can still assign the returned value to a variable with a wildcard type; but you just don't have to).
Upvotes: 2