该方法field.getGenericType()
返回对Type
接口的引用。真正的类型可能是TypeVariable
or GenericArrayType
or ParameterizedType
or Class
or 的一个实例,或者我目前不知道的其他东西。
需要不同的方法来检索字段的实际类型。
这是我以 TypeFieldTreeNode 对象树的形式获取有关公共字段信息的解决方案。
public class TypeFieldTreeNode {
public String fieldName;
public String typeSimpleName;
public String typeCanonicalName;
public String typeGenericName;
public List<TypeFieldTreeNode> children;
public TypeFieldTreeNode(String fieldName, String typeSimpleName, String typeCanonicalName, String genericTypeName) {
this.fieldName = fieldName;
this.typeSimpleName = typeSimpleName;
this.typeCanonicalName = typeCanonicalName;
this.typeGenericName = genericTypeName;
this.children = new ArrayList<>();
}
}
主要方法:
private List<TypeFieldTreeNode> getTypeFields(Class<?> clazz, Type genericType,
Map<TypeVariable<?>, Type> actualClassArguments) throws Exception {
if(clazz == null) {
return Collections.emptyList();
}
List<Field> fields = Arrays.stream(clazz.getDeclaredFields())
.filter(f -> Modifier.isPublic(f.getModifiers()) && !Modifier.isFinal(f.getModifiers()))
.collect(Collectors.toList());
List<TypeFieldTreeNode> result = new ArrayList<>();
Map<TypeVariable<?>, Type> classArgumentsMap = mapTypeActualClassArguments(
clazz, genericType, actualClassArguments);
for(Field field : fields) {
result.add(getClassFieldData(field, classArgumentsMap));
}
if(clazz.getSuperclass() != null) {
List<TypeFieldTreeNode> superClassFields =
getTypeFields(clazz.getSuperclass(), clazz.getGenericSuperclass(), classArgumentsMap);
result.addAll(superClassFields);
}
return result;
}
接下来是一个核心方法的列表,它将泛型参数的 TypeVariable 类型的元数据与泛型参数的实际类型绑定。当泛型参数的类型是 TypeVariable 的实例时,方法使用之前获得的映射来恢复该泛型参数的实际类型:
private Map<TypeVariable<?>, Type> mapTypeActualClassArguments(Class<?> clazz, Type genericType,
Map<TypeVariable<?>, Type> actualClassArguments) throws Exception {
if(!(genericType instanceof ParameterizedType)) {
return Collections.emptyMap();
}
Map<TypeVariable<?>, Type> result = new HashMap<>();
Type[] actualTypeParametersTypes = ((ParameterizedType) genericType).getActualTypeArguments();
TypeVariable<?>[] classTypeParameters = clazz.getTypeParameters();
for (int i = 0; i < classTypeParameters.length; i++) {
if(actualTypeParametersTypes[i] instanceof TypeVariable<?>) {
TypeVariable<?> fieldTypeVariable = (TypeVariable<?>) actualTypeParametersTypes[i];
if(actualClassArguments.containsKey(fieldTypeVariable))
actualTypeParametersTypes[i] = actualClassArguments.get(fieldTypeVariable);
else
throw new Exception(String.format("For generic parameter %s of type %s, the corresponding actual type of generic parameter was not found",
classTypeParameters[i].getName(), genericType.getTypeName()));
}
result.put(classTypeParameters[i], actualTypeParametersTypes[i]);
}
return result;
}
获取有关字段的数据以及作为该字段类型的类的所有可用字段:
private TypeFieldTreeNode getClassFieldData(Field field,
Map<TypeVariable<?>, Type> actualClassArguments) throws Exception {
Class<?> fieldClass = field.getType();
Type fieldGenericType = field.getGenericType();
TypeFieldTreeNode result = null;
// if type of the field is a generic parameter of the class containing the field
if(fieldGenericType instanceof TypeVariable<?>) {
Type actualFieldType = null;
Class<?> actualFieldClass = null;
Map<TypeVariable<?>, Type> fieldTypeActualClassArguments = new HashMap<>();
TypeVariable<?> fieldTypeVariable = (TypeVariable<?>) fieldGenericType;
if(actualClassArguments.containsKey(fieldTypeVariable))
actualFieldType = actualClassArguments.get(fieldTypeVariable);
else
throw new Exception(String.format("For a field %s of type %s from class %s, the corresponding actual type of generic parameter was not found",
field.getName(), fieldGenericType.getTypeName(), field.getDeclaringClass().getCanonicalName()));
// for example, field "myField2" of class MyClass2<MyClass<Integer>> where:
// public class MyClass2<T> { public T myField2; }
// public class MyClass<T> { public T myField; }
if(actualFieldType instanceof ParameterizedType) {
actualFieldClass = (Class<?>)((ParameterizedType) actualFieldType).getRawType();
result = new TypeFieldTreeNode(field.getName(), actualFieldClass.getSimpleName(),
actualFieldClass.getCanonicalName(), actualFieldType.getTypeName());
fieldTypeActualClassArguments = mapTypeActualClassArguments(actualFieldClass, actualFieldType, actualClassArguments);
}
// for example, field "myField" of class MyClass<Integer> where:
// public class MyClass<T> { public T myField; }
else {
actualFieldClass = (Class<?>) actualFieldType;
result = new TypeFieldTreeNode(field.getName(), actualFieldClass.getSimpleName(),
actualFieldClass.getCanonicalName(), "");
}
List<Field> childFields = Arrays.stream(actualFieldClass.getFields())
.filter(f -> !Modifier.isFinal(f.getModifiers()))
.collect(Collectors.toList());
for (Field childField : childFields) {
result.children.add(getClassFieldData(childField, fieldTypeActualClassArguments));
}
}
// if the field is an array and the type of the elements of the array is a generic parameter of the class containing the field
// for example, field "myField" of class MyClass<Integer> where:
// public class MyClass<T> { public T[] myField; }
else if(fieldGenericType instanceof GenericArrayType) {
Type genericComponentType = ((GenericArrayType) fieldGenericType).getGenericComponentType();
if(genericComponentType instanceof TypeVariable<?>) {
if(actualClassArguments.containsKey(genericComponentType)) {
Type actualArrayComponentType = actualClassArguments.get(genericComponentType);
assert !(actualArrayComponentType instanceof ParameterizedType);
Class<?> actualArrayClass = (Class<?>) actualArrayComponentType;
result = new TypeFieldTreeNode(field.getName(), actualArrayClass.getSimpleName() + "[]",
actualArrayClass.getCanonicalName() + "[]", "");
}
else
throw new Exception(String.format("For a field %s of type %s from class %s, the corresponding actual type of generic parameter was not found",
field.getName(), fieldGenericType.getTypeName(), field.getDeclaringClass().getCanonicalName()));
}
else
throw new Exception(String.format("Unknown array genericComponentType: %s", genericComponentType.getClass().getCanonicalName()));
}
else {
result = new TypeFieldTreeNode(field.getName(), fieldClass.getSimpleName(), fieldClass.getCanonicalName(), "");
Map<TypeVariable<?>, Type> fieldTypeActualClassArguments = new HashMap<>();
// for example, field "myField2" of class MyClass2<Integer> where:
// public class MyClass2<T> { public MyClass<T> myField2; }
// public class MyClass<T> { public T myField; }
if(fieldGenericType instanceof ParameterizedType) {
// custom generic type name creator for situations when actual type arguments can be of type TypeVariable
result.typeGenericName = getGenericTypeName((ParameterizedType)fieldGenericType, actualClassArguments);
fieldTypeActualClassArguments = mapTypeActualClassArguments(fieldClass, fieldGenericType, actualClassArguments);
}
List<Field> childFields = Arrays.stream(fieldClass.getFields()).filter(f -> !Modifier.isFinal(f.getModifiers()))
.collect(Collectors.toList());
for (Field childField : childFields) {
result.children.add(getClassFieldData(childField, fieldTypeActualClassArguments));
}
}
return result;
}
private String getGenericTypeName(ParameterizedType parameterizedType,
Map<TypeVariable<?>, Type> actualClassArguments) throws Exception {
List<String> genericParamJavaTypes = new ArrayList<>();
for(Type typeArgument : parameterizedType.getActualTypeArguments()) {
if (typeArgument instanceof TypeVariable<?>) {
TypeVariable<?> typeVariable = (TypeVariable<?>) typeArgument;
if(actualClassArguments.containsKey(typeVariable)) {
typeArgument = actualClassArguments.get(typeVariable);
} else
throw new Exception(String.format("For generic parameter %s of type %s, the corresponding actual type of generic parameter was not found",
typeArgument.getTypeName(), parameterizedType.getTypeName()));
}
if(typeArgument instanceof ParameterizedType) {
ParameterizedType parameterizedTypeArgument = (ParameterizedType) typeArgument;
Map<TypeVariable<?>, Type> typeActualClassArguments = mapTypeActualClassArguments(
(Class<?>)parameterizedTypeArgument.getRawType(),
typeArgument, actualClassArguments);
genericParamJavaTypes.add(getGenericTypeName((ParameterizedType) typeArgument, typeActualClassArguments));
}
else if (typeArgument instanceof Class<?>)
genericParamJavaTypes.add(((Class<?>) typeArgument).getCanonicalName());
else
throw new Exception(String.format("For generic parameter %s of type %s, the corresponding actual type of generic parameter was not found", typeArgument.getTypeName()));
}
Class<?> rawType = (Class<?>) parameterizedType.getRawType();
return rawType.getCanonicalName() + "<" + String.join(", ", genericParamJavaTypes) + ">";
}
用法:
public List<TypeFieldTreeNode> getReturnTypeFields(Method method) throws Exception {
return getTypeFields(method.getReturnType(),
method.getGenericReturnType(), Collections.emptyMap());
}
该解决方案对以下测试类型按预期工作:
MyClass2<MyClass<Integer>, MyClass<Boolean>, Double>
MyClass3<MyClass<Integer>, MyClass<Double>>
在哪里:
public class MyClass<T> {
public T value;
public List<String> list;
}
public class MyClass2<T, V, E> {
public T value;
public List<String> strList;
public List<V> genericList;
public int[] intArray;
public E[] genericArray;
public MyClass<E> genericClass;
}
public class MyClass3<T, V> extends MyClass2<T, V, Boolean> {
public T value3;
public List<V> genericList3;
}