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.