I wants to achieve something like this in java, but getting compile time error:
The method onMessage(TextMessage, Class) in the type AbstractTestLoader is not applicable for the arguments (TextMessage, Class)
I understand the reason of that error, but I also feel there should be some way to achieve this with casting or may be some other way.
public abstract class AbstractTestLoader<T extends AbstractEntity<T>> {
public void onMessage(TextMessage message) throws Exception {
onMessage(message, this.getClass()); // I want to correct this line in such a way so that I can call below method with actual Class of {T extends AbstractEntity<T>}
}
public void onMessage(TextMessage message, Class<T> clazz) throws Exception {
//here my original logic will go
}
}
It should be noticed that while Java generic got erased at runtime, there are limited reflection apis to retrieve them if they are present in the class file.
Here is a quick solution with these assumption:
AbstractTestLoader
is the direct super class.class GenericLoader<T extends SubclassAbstractEntity<T>> extends AbstractTestLoader<T>
does not exist.Here is the code:
public class AbstractTestLoader<T extends AbstractEntity<T>> {
private static final ClassValue<Class<?>> TYPE_PARAMETER_CACHE = new ClassValue<Class<?>>() {
@Override
protected Class<?> computeValue(Class<?> type) {
assert AbstractTestLoader.class.isAssignableFrom(type);
assert type.getSuperclass() == AbstractTestLoader.class;
Type genericSuperclass = type.getGenericSuperclass();
assert genericSuperclass instanceof ParameterizedType;
Type entityType = ((ParameterizedType) genericSuperclass).getActualTypeArguments()[0];
assert entityType instanceof Class<?>;
return (Class<?>) entityType;
}
};
@SuppressWarnings("unchecked")
protected Class<T> getEntityType() {
return (Class<T>) TYPE_PARAMETER_CACHE.get(this.getClass());
}
public void onMessage(Object message) throws Exception {
onMessage(message, getEntityType()); // getting compile time error here
}
public void onMessage(Object message, Class<T> clazz) throws Exception {
//here my original logic will go
}
}
The getEntityType
can be overriden in subclasses where those two assumptions fail.
Finally, after couple of tries I just get simple and working solution, but still I am open to hear other best answers if possible. Thanks
public abstract class AbstractTestLoader<T extends AbstractEntity<T>> {
abstract Class<T> getEntityType();
public void onMessage(TextMessage message) throws Exception {
onMessage(message, getEntityType());
}
public void onMessage(TextMessage message, Class<T> clazz) throws Exception {
//here my original logic will go
}
}
Java implements generics via type erasure, which means that it's just a compile-time concept. When the program is running, there's no difference between an AbstractTestLoader<Foo>
and a AbstractTestLoader<Bar>
.
There are a few workarounds, like the one you discovered, where a class that's aware of the type of T
at compile time can be made to provide an actual class type. But there's no way using pure generics to discover what the generic type is at runtime.
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