This is my code, shared here, so you can run it.
public interface GenericRepository<T> {
default Class<T> getEntityClazz() {
ParameterizedType pType = (ParameterizedType)this.getClass().getGenericInterfaces()[0];
Type tT = pType.getActualTypeArguments()[0];
Class<T> clazz = (Class<T>) tT.getClass();
return clazz;
}
}
public class GenericRepositoryImpl<T> implements GenericRepository<T> {
}
My main is:
public class Main
{
public static void main(String[] args) {
GenericRepository<Example> genericObject = new GenericRepositoryImpl();
Class<Example> clazz = genericObject.getEntityClazz();
System.out.println("Generic class: " clazz.toString());
}
}
And output is:
Generic class: class sun.reflect.generics.reflectiveObjects.TypeVariableImpl
I was expecting to get Generic class: class Example
.
Any ideas?
CodePudding user response:
In Java you deal with generic type erasure, so...
public interface GenericRepository<T> {
Class<T> getEntityClazz();
}
//declared as abstract so override of getEntityClazz() is not required here
public abstract class GenericRepositoryImpl<T> implements GenericRepository<T> {
//common fields and logic
}
public class ExampleReposirory extends GenericRepositoryImpl<Example> {
//constructor calling super
@Override
public Class<T> getEntityClazz() {
return Example.class;
}
}
This approach is rather verbose, but at least wins in clarity and straightforwardness compared to the magic of reflection
CodePudding user response:
When you use this.getClass()
, you're basically dealing with the definition of the class, not the INSTANCE which holds the generic information you may need.
If you declaring you generic type like A<T extends B>
, then you can retrieve the type of B
by using ((TypeVariableImpl)pType.getActualTypeArguments()[0]).getBounds()[0]
, but I don't think that's what you want to do.
So I would suggest 2 other alternatives:
1, Just return the class type in your implementation class, in with the generic type is explicitly specified:
public class GenericRepositoryImpl implements GenericRepository<Example> {
@Override
public Class<Example> getEntityClazz() {
return Example.class;
}
}
2, When you still want to get the type of generic type in the generic class itself, then save it somewhere when you initialize the object, the trade-off is that you have to put that type in as an argument to initialize:
public class GenericRepositoryImpl<T> implements GenericRepository<T> {
private final Class<T> type;
public GenericRepositoryImpl(Class<T> type) {
this.type = type;
}
@Override
public Class<T> getEntityClazz() {
return this.type;
}
}
And the usage would be like this :
public class Main {
public static void main(String[] args) {
GenericRepository<Example> genericObject = new GenericRepositoryImpl(Example.class);
System.out.println("Generic class: " genericObject.getEntityClazz().toString());
}
}