Reputation: 52
I have a spring project with several classes "IceCream", "Chocolate" that implements an interface "FoodInterface". FoodInterface:
public interface FoodInterface{
String getType();
}
IceCream:
public class IceCream implements FoodInterface{
@Override
public String getType() { return "ice-cream"; }
}
I also have repositories for these classes:
public interface IceCreamRepository extends JpaRepository<IceCream, String>, FoodRepositoryInterface<IceCream> {
IceCream findAllById(String id)
}
To select the correct repository when having the type string (e.g. ice-cream
) and the string id i have created a FoodRepositoryInterface:
public interface FoodRepositoryInterface<T extends FoodInterface> {
String getType() {}
}
and I changed my repositories to this:
public interface IceCreamRepository extends JpaRepository<IceCream, String>, FoodRepositoryInterface<IceCream> {
String getType() { return "ice-cream"; }
IceCream findAllById(String id)
}
But I want to be able to have ice-cream
in one place (in the IceCream class and the same for the Chocolate class).
Therefore I want to get the class of the generic type, call the constructor and call getType() all in the FoodRepositoryInterface. return new T().getType()
does not work so I wanted to use reflections. And here i followed Get type of a generic parameter in Java with reflection which resulted in this mess:
public interface FoodRepositoryInterface<T extends FoodInterface> {
default String getType() {
var genericTypeClass = getClass();
var foodRepositoryInterfaceClass = genericTypeClass.getGenericSuperclass();
var castedFoodRepositoryInterfaceClass = (ParameterizedType) foodRepositoryInterfaceClass;
var arguments = castedFoodRepositoryInterfaceClass.getActualTypeArguments();
Class<T> persistentClass = (Class<T>) arguments[0];
try {
return persistentClass.getConstructor().newInstance().getType();
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
};
}
Running that, I got java.lang.ClassCastException: class java.lang.Class cannot be cast to class java.lang.reflect.ParameterizedType (java.lang.Class and java.lang.reflect.ParameterizedType are in module java.base of loader 'bootstrap')
in the last code block. So I looked onto the classes that were actually in the variables:
I expected IceCream
and FoodRepositoryInterface
but not Proxy
and Proxy151
. It explains why the casting fails. I think this has something to do with Spring and Spring AOP. In the Proxy variable I couldn't determine any hint to the IceCream class or the FoodRepositoryInterface class.
What do I need to do here to get my IceCream.class?
Upvotes: -1
Views: 139
Reputation: 52
I solved it with another workaround:
Class<? extends FoodRepositoryInterface> genericTypeClass = getClass();
Class<T> persistentClass = (Class<T>) GenericTypeResolver.resolveType(FoodRepositoryInterface.class.getTypeParameters()[0], genericTypeClass);
The GenericTypeResolver is from org.springframework.core
. The workaround is based on this post: https://stackoverflow.com/a/9202329/18427492
I still don't understand the proxy-thing but this workaround is fine for me. Maybe someone can provide a solution for others that can't use the GenericTypeResolver.
Upvotes: 0