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.