I have an abstract class that looks like this:
abstract class Handler<T> {
Handler(Class<T> clazz) {
// ...
}
abstract void handle(T object);
}
I'm trying to extent it, where T
is a type with a wildcard generic parameter (for the sake of the example, say List<?>
). What I want to be able to do is something like:
class MyHandler extends Handler<List<?>> {
MyHandler() {
super(List.class);
// ^ Compiler error: The constructor Handler<List<?>>(Class<List>) is undefined
// super(List<?>.class); is also a compiler error
}
void handle(List<?> object) {
// ...
}
}
As far as I can tell the above is totally safe, so I'm not sure why the compiler doesn't allow it. My current solution involves the use of raw types, unsafe casting and suppression of the warnings and seems like it can't be solution the language intends me to use:
class MyHandler extends Handler<List> { // Warning: List is a raw type
MyHandler() {
super(List.class);
}
void handle(List objectRaw) { // Warning: List is a raw type
List<?> object = (List<?>) objectRaw;
// ...
}
}
This needs to be a singleton so I can't add generic parameters to MyHandler
. How do I avoid all these bad practices (raw types and the cast)? Theres no reason this should be unsafe and I'm having a hard time believing there's no way to do this in Java.
CodePudding user response:
You can solve the problem by casting the class object to type Class<List<?>>
in the MyHandler
constructor:
MyHandler() {
super((Class<List<?>>) (Object) List.class);
}
The problem
The problem is that there is no simple way in Java to express a class literal of type Class<List<?>>
. List.class
has type Class<List>
.
I think there are situations where it would not be safe to the second type to the first one, but in your code there is probable no problem at all.
If, for example, your code uses clazz
to check the runtime type of objects using isAssignableFrom
you might get it trouble.
Type token
If you use some type-token implementation instead of a java.lang.Class
, for example the one in Guava, you will be able to express a literal of the exact right type:
abstract class Handler<T> {
Handler(TypeToken<T> clazz) {
// ...
}
abstract void handle(T object);
}
class MyHandler extends Handler<List<?>> {
MyHandler() {
super(new TypeToken<List<?>>() {});
}
void handle(List<?> object) {
// ...
}
}
CodePudding user response:
You don't need to pass a class object to the Handler
constructor.
abstract class Handler<T> {
final Class<T> clazz;
@SuppressWarnings("unchecked")
Handler(T... dummy) {
if (dummy == null || dummy.length > 0)
throw new IllegalArgumentException("Do not specify the 'dummy' argument");
clazz = (Class<T>) dummy.getClass().componentType();
System.out.println("Handler clazz=" clazz);
}
abstract void handle(T object);
}
class MyHandler extends Handler<List<Integer>> {
void handle(List<Integer> object) {
// ...
}
}
and
MyHandler mh = new MyHandler();
output
Handler clazz=interface java.util.List
You can also implement anonymous inner classes.
Handler<String> h = new Handler<>() {
@Override
void handle(String object) {
// TODO Auto-generated method stub
}
};
output
Handler clazz=class java.lang.String