Да, это возможно.
Вы можете создать Map<TypeVariable<?>, Class<?>>
и пройти граф типов, начиная с указанного корневого класса, чтобы заполнить конкретные типы, предоставленные для каждой переменной типа.Например:
public static Map<TypeVariable<?>, Class<?>> findTypeParameterValues(Class<?> cls) {
Map<TypeVariable<?>, Class<?>> parameterMap = new HashMap<>();
findTypeParameterValues(parameterMap, cls);
return parameterMap;
}
private static void findTypeParameterValues(Map<TypeVariable<?>, Class<?>> map, Class<?> cur) {
// create list of parameterized super types
List<ParameterizedType> pTypes = genericSuperTypes(cur)
.filter(ParameterizedType.class::isInstance)
.map(ParameterizedType.class::cast)
.collect(Collectors.toList());
for(ParameterizedType pType : pTypes) {
Type[] tArgs = pType.getActualTypeArguments(); // provided type arguments
Class<?> rawType = (Class<?>) pType.getRawType(); // (always a Class<?>)
TypeVariable<?>[] tParams = rawType.getTypeParameters(); // type parameters
for(int i = 0; i < tArgs.length; i++) { // iterate over both
Type tArg = tArgs[i]; // match type argument...
TypeVariable<?> tParam = tParams[i]; // with type parameter
Class<?> arg; // the value for the parameter
if(tArg instanceof Class<?>) {
// concrete argument
arg = (Class<?>) tArg;
} else if(tArg instanceof TypeVariable<?>) {
// concrete argument provided previously, or null
arg = map.get((TypeVariable<?>) tArg);
} else {
throw new UnsupportedOperationException("Unsupported type argument type: " + tArg);
}
map.put(tParam, arg); // put found value in map
}
}
superClasses(cur).forEach(pt -> findTypeParameterValues(map, pt));
}
private static Stream<Class<?>> superClasses(Class<?> cls) {
Stream.Builder<Class<?>> ret = Stream.builder();
ret.add(cls.getSuperclass());
Arrays.stream(cls.getInterfaces()).forEach(ret::add);
return ret.build().filter(Objects::nonNull);
}
private static Stream<Type> genericSuperTypes(Class<?> cls) {
Stream.Builder<Type> ret = Stream.builder();
ret.add(cls.getGenericSuperclass());
Arrays.stream(cls.getGenericInterfaces()).forEach(ret::add);
return ret.build().filter(Objects::nonNull);
}
Затем, чтобы использовать результат для вашего конкретного случая, вы можете получить конкретное значение для переменной типа List
с карты:
public static void main(String[] args) throws Throwable {
System.out.println(getGenericListItemType(B.class)); // class java.lang.String
System.out.println(getGenericListItemType(A.class)); // class java.lang.String
System.out.println(getGenericListItemType(D.class)); // class java.lang.Integer
}
private static final TypeVariable<?> listE = List.class.getTypeParameters()[0];
public static Class<?> getGenericListItemType(Class<?> cls) {
// method defined in 'Reflection' helper class
return Reflection.findTypeParameterValues(cls).get(listE);
}
Обратите внимание, что еслинет конкретного определения для конкретной переменной типа, карта будет содержать null
в качестве значения для переменной типа.