Let's suppose we have a type CustomMap<A, B>
which extends CommonMap<C, D>
which in turn implements Map<E, F>
.
Naturally one would expect that A == C == E
, but it's not always so - you can have a CustomMap<V>
implementing Map<String, V>
for example.
The question is, let's suppose I have a Field
, and field.getType()
is some interface or class implementing Map<K, V>
. The type itself may be non generic, may be generic but with generic signature differing from <K, V>
, etc. How go I get K
and V
type parameters of Map
using reflection?
CodePudding user response:
It seems that one will have to write code which crawls type information, remembers which type corresponds to type parameter names, and then do the substitution. The code may vary depending on the specifics, such as class/interface, etc, but the core of it is:
// Contains mappings such as K -> Integer, V -> String
Map<String, Type> typeParameters = new HashMap<>();
ParameterizedType pType = (ParameterizedType) type;
// Actual type arguments of a specific parametrized type, e.g. <Integer, PARAM> -
// the latter is resolved against typeParameters map
Type[] typeArguments = pType.getActualTypeArguments();
cls = ((Class) ((ParameterizedType) type).getRawType());
// Named arguments of type as declared, e.g. <K, V>
TypeVariable<?>[] typeParamDecls = cls.getTypeParameters();
for (int i = 0; i < Math.min(typeParamDecls.length, typeArguments.length); i ) {
Type value;
if (typeArguments[i] instanceof TypeVariable) {
value = typeParameters.get(((TypeVariable<?>) typeArguments[i]).getName());
} else {
value = typeArguments[i];
}
typeParameters.put(typeParamDecls[i].getName(), value);
}
This needs to be applied recursively to cls.getGenericSuperclass()
/ cls.getGenericInterfaces()
until you find the type that you wanted in the first place. There may be edge cases if raw types are used anywhere.