I am trying to get the generic type of a class or interface implementation. I am aware that this has some risks and quirks, but I'm trying to understand what is possible.
Here's my example code:
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.LinkedList;
public class Main {
public interface MyInterface<T> {
}
public static class BaseImpl<T> implements MyInterface<T> {
}
public static class TypedImpl extends BaseImpl<Integer> {
}
public static void main(String[] args) throws Exception {
LinkedList<MyInterface<Integer>> instances = new LinkedList<>();
// 1. anonymous class from interface
instances.add(new MyInterface<Integer>() {
});
// 2. class extending interface
instances.add(new BaseImpl<Integer>());
// 3. class with pre-defined generic type
instances.add(new TypedImpl());
for (MyInterface<Integer> instance : instances) {
System.out.println("----------------------------------------------");
Class clazz = instance.getClass();
Type genericSuper = clazz.getGenericSuperclass();
Type[] genericInterfaces = clazz.getGenericInterfaces();
Class target = null;
System.out.println("class: " + clazz.getName());
System.out.println("generic super: " + genericSuper);
System.out.println("generic interfaces: " + Arrays.asList(genericInterfaces));
// attempt to 'extract' generic type
if (genericSuper instanceof ParameterizedType) {
target = getGeneric((ParameterizedType) genericSuper);
} else if (genericInterfaces.length > 0) {
for (Type genericInterface : genericInterfaces) {
if (genericInterface instanceof ParameterizedType) {
target = getGeneric((ParameterizedType) genericInterface);
if (null != target) {
break;
}
}
}
}
System.out.println("TARGET: " + target);
}
}
// attempt to get type argument
public static Class getGeneric(ParameterizedType type) {
if (MyInterface.class.isAssignableFrom((Class) type.getRawType())) {
Type typeArg = type.getActualTypeArguments()[0];
try {
return (Class) typeArg;
} catch (ClassCastException e) {
System.out.println("cast exception for '" + typeArg + "'");
}
}
return null;
}
}
The output of this is:
----------------------------------------------
class: Main$1
generic super: class java.lang.Object
generic interfaces: [Main.Main$MyInterface<java.lang.Integer>]
TARGET: class java.lang.Integer
----------------------------------------------
class: Main$BaseImpl
generic super: class java.lang.Object
generic interfaces: [Main.Main$MyInterface<T>]
cast exception for 'T'
TARGET: null
----------------------------------------------
class: Main$TypedImpl
generic super: Main.Main$BaseImpl<java.lang.Integer>
generic interfaces: []
TARGET: class java.lang.Integer
So, my goal is to have the value of the target
variable be Integer.class
. For the anonymous class (#1) and the explicitly typed implementation (#3) I am able to find the target as expected. Why does it work in these instances and not in the case of #2 (BaseImpl instance)? Is there another way to accomplish this? (I am aware of the common workaround of passing the target Class
via the implementing class's constructor, however I'm interested in doing this dynamically)
I have consulted the following sources:
- Get type of a generic parameter in Java with reflection
- Get generic type of class at runtime
- How to get a class instance of generics type T
Thanks a lot in advance,
Abel