[英]How can I determine the type of a generic field in Java?
我一直在尝试确定类中字段的类型。 我已经看到了所有的内省方法,但还没有完全弄清楚如何去做。 这将用于从 Java 类生成 xml/json。 我在这里查看了许多问题,但还没有找到我需要的确切内容。
例子:
class Person {
public final String name;
public final List<Person> children;
}
当我编组这个对象时,我需要知道chidren
字段是一个Person
类型的对象列表,所以我可以正确地编组它。
我试过了
for (Field field : Person.class.getDeclaredFields()) {
System.out.format("Type: %s%n", field.getType());
}
但这只会告诉我这是一个List
,而不是List
of Person
s
谢谢
从 Java Tutorial Trail: The Reflection API 中查看获取字段类型。
基本上,您需要做的是获取类的所有java.lang.reflect.Field
并在每个
类上
调用
(检查下面的编辑)。 要获取所有对象字段,包括公共、受保护、包和私有访问字段,只需使用Field#getType()
Class.getDeclaredFields()
。 像这样的东西:
for (Field field : Person.class.getDeclaredFields()) {
System.out.format("Type: %s%n", field.getType());
System.out.format("GenericType: %s%n", field.getGenericType());
}
编辑:正如wowest在评论中指出的那样,您实际上需要调用Field#getGenericType()
,检查返回的Type
是否为ParameterizedType
,然后相应地获取参数。 使用ParameterizedType#getRawType()
和ParameterizedType#getActualTypeArgument()
得到的原始类型和的类型自变量的数组ParameterizedType
分别。 以下代码演示了这一点:
for (Field field : Person.class.getDeclaredFields()) {
System.out.print("Field: " + field.getName() + " - ");
Type type = field.getGenericType();
if (type instanceof ParameterizedType) {
ParameterizedType pType = (ParameterizedType)type;
System.out.print("Raw type: " + pType.getRawType() + " - ");
System.out.println("Type args: " + pType.getActualTypeArguments()[0]);
} else {
System.out.println("Type: " + field.getType());
}
}
并且会输出:
Field: name - Type: class java.lang.String
Field: children - Raw type: interface java.util.List - Type args: class foo.Person
这是一个回答我的问题的例子
class Person {
public final String name;
public final List<Person> children;
}
//in main
Field[] fields = Person.class.getDeclaredFields();
for (Field field : fields) {
Type type = field.getGenericType();
System.out.println("field name: " + field.getName());
if (type instanceof ParameterizedType) {
ParameterizedType ptype = (ParameterizedType) type;
ptype.getRawType();
System.out.println("-raw type:" + ptype.getRawType());
System.out.println("-type arg: " + ptype.getActualTypeArguments()[0]);
} else {
System.out.println("-field type: " + field.getType());
}
}
这输出
field name: name -field type: class java.lang.String field name: children -raw type:interface java.util.List -type arg: class com.blah.Person
我还没有找到任何通过继承层确定泛型字段类型的框架,所以我写了一些方法:
该逻辑通过字段信息和当前对象类来确定类型。
清单 1 - 逻辑:
public static Class<?> determineType(Field field, Object object) {
Class<?> type = object.getClass();
return (Class<?>) getType(type, field).type;
}
protected static class TypeInfo {
Type type;
Type name;
public TypeInfo(Type type, Type name) {
this.type = type;
this.name = name;
}
}
private static TypeInfo getType(Class<?> clazz, Field field) {
TypeInfo type = new TypeInfo(null, null);
if (field.getGenericType() instanceof TypeVariable<?>) {
TypeVariable<?> genericTyp = (TypeVariable<?>) field.getGenericType();
Class<?> superClazz = clazz.getSuperclass();
if (clazz.getGenericSuperclass() instanceof ParameterizedType) {
ParameterizedType paramType = (ParameterizedType) clazz.getGenericSuperclass();
TypeVariable<?>[] superTypeParameters = superClazz.getTypeParameters();
if (!Object.class.equals(paramType)) {
if (field.getDeclaringClass().equals(superClazz)) {
// this is the root class an starting point for this search
type.name = genericTyp;
type.type = null;
} else {
type = getType(superClazz, field);
}
}
if (type.type == null || type.type instanceof TypeVariable<?>) {
// lookup if type is not found or type needs a lookup in current concrete class
for (int j = 0; j < superClazz.getTypeParameters().length; ++j) {
TypeVariable<?> superTypeParam = superTypeParameters[j];
if (type.name.equals(superTypeParam)) {
type.type = paramType.getActualTypeArguments()[j];
Type[] typeParameters = clazz.getTypeParameters();
if (typeParameters.length > 0) {
for (Type typeParam : typeParameters) {
TypeVariable<?> objectOfComparison = superTypeParam;
if(type.type instanceof TypeVariable<?>) {
objectOfComparison = (TypeVariable<?>)type.type;
}
if (objectOfComparison.getName().equals(((TypeVariable<?>) typeParam).getName())) {
type.name = typeParam;
break;
}
}
}
break;
}
}
}
}
} else {
type.type = field.getGenericType();
}
return type;
}
清单 2 - 样本/测试:
class GenericSuperClass<E, T, A> {
T t;
E e;
A a;
BigDecimal b;
}
class GenericDefinition extends GenericSuperClass<Integer, Integer, Integer> {
}
@Test
public void testSimpleInheritanceTypeDetermination() {
GenericDefinition gd = new GenericDefinition();
Field field = ReflectionUtils.getField(gd, "t");
Class<?> clazz = ReflectionUtils.determineType(field, gd);
Assert.assertEquals(clazz, Integer.class);
field = ReflectionUtils.getField(gd, "b");
clazz = ReflectionUtils.determineType(field, gd);
Assert.assertEquals(clazz, BigDecimal.class);
}
class MiddleClass<A, E> extends GenericSuperClass<E, Integer, A> { }
// T = Integer, E = String, A = Double
class SimpleTopClass extends MiddleClass<Double, String> { }
@Test
public void testSimple2StageInheritanceTypeDetermination() {
SimpleTopClass stc = new SimpleTopClass();
Field field = ReflectionUtils.getField(stc, "t");
Class<?> clazz = ReflectionUtils.determineType(field, stc);
Assert.assertEquals(clazz, Integer.class);
field = ReflectionUtils.getField(stc, "e");
clazz = ReflectionUtils.determineType(field, stc);
Assert.assertEquals(clazz, String.class);
field = ReflectionUtils.getField(stc, "a");
clazz = ReflectionUtils.determineType(field, stc);
Assert.assertEquals(clazz, Double.class);
}
class TopMiddleClass<A> extends MiddleClass<A, Double> { }
// T = Integer, E = Double, A = Float
class ComplexTopClass extends TopMiddleClass<Float> {}
@Test void testComplexInheritanceTypDetermination() {
ComplexTopClass ctc = new ComplexTopClass();
Field field = ReflectionUtils.getField(ctc, "t");
Class<?> clazz = ReflectionUtils.determineType(field, ctc);
Assert.assertEquals(clazz, Integer.class);
field = ReflectionUtils.getField(ctc, "e");
clazz = ReflectionUtils.determineType(field, ctc);
Assert.assertEquals(clazz, Double.class);
field = ReflectionUtils.getField(ctc, "a");
clazz = ReflectionUtils.determineType(field, ctc);
Assert.assertEquals(clazz, Float.class);
}
class ConfusingClass<A, E> extends MiddleClass<E, A> {}
// T = Integer, E = Double, A = Float ; this class should map between a and e
class TopConfusingClass extends ConfusingClass<Double, Float> {}
@Test
public void testConfusingNamingConvetionWithInheritance() {
TopConfusingClass tcc = new TopConfusingClass();
Field field = ReflectionUtils.getField(tcc, "t");
Class<?> clazz = ReflectionUtils.determineType(field, tcc);
Assert.assertEquals(clazz, Integer.class);
field = ReflectionUtils.getField(tcc, "e");
clazz = ReflectionUtils.determineType(field, tcc);
Assert.assertEquals(clazz, Double.class);
field = ReflectionUtils.getField(tcc, "a");
clazz = ReflectionUtils.determineType(field, tcc);
Assert.assertEquals(clazz, Float.class);
field = ReflectionUtils.getField(tcc, "b");
clazz = ReflectionUtils.determineType(field, tcc);
Assert.assertEquals(clazz, BigDecimal.class);
}
class Pojo {
Byte z;
}
@Test
public void testPojoDetermineType() {
Pojo pojo = new Pojo();
Field field = ReflectionUtils.getField(pojo, "z");
Class<?> clazz = ReflectionUtils.determineType(field, pojo);
Assert.assertEquals(clazz, Byte.class);
}
我期待听到您的反馈!
拿这个片段:
for (Field field : Person.class.getFields()) {
System.out.println(field.getType());
}
关键类是Field
这是我的看法。 它无法处理所有可能的情况(并且肯定有一些错误),但它确实处理了到目前为止我的代码中出现的所有情况。 这包括这些声明,这对于许多用例来说应该是一个好的开始:
private int primitiveField1;
private Object field1;
private List<Integer> field2;
private Map<Integer, String> field3;
private Map<? extends String, List<Map<Class<?>, Object>>> field4;
private char[] array1;
private Character[] array2;
private Class<? extends Integer>[] array3;
private List<Integer>[] array4;
private InnerClass<String> innerClass;
执行:
public static String getDeclaration(Field field) {
return getDeclaration(field.getGenericType());
}
private static String getDeclaration(Type genericType) {
if(genericType instanceof ParameterizedType) {
// types with parameters
ParameterizedType parameterizedType = (ParameterizedType) genericType;
String declaration = parameterizedType.getRawType().getTypeName();
declaration += "<";
Type[] typeArgs = parameterizedType.getActualTypeArguments();
for(int i = 0; i < typeArgs.length; i++) {
Type typeArg = typeArgs[i];
if(i > 0) {
declaration += ", ";
}
// note: recursive call
declaration += getDeclaration(typeArg);
}
declaration += ">";
declaration = declaration.replace('$', '.');
return declaration;
}
else if(genericType instanceof Class<?>) {
Class<?> clazz = (Class<?>) genericType;
if(clazz.isArray()) {
// arrays
return clazz.getComponentType().getCanonicalName() + "[]";
}
else {
// primitive and types without parameters (normal/standard types)
return clazz.getCanonicalName();
}
}
else {
// e.g. WildcardTypeImpl (Class<? extends Integer>)
return genericType.getTypeName();
}
}
正如 dfa 指出的那样,您可以使用java.lang.reflect.Field.getType
获得擦除的类型。 您可以使用Field.getGenericType
获取泛型类型(它可能有通配符和绑定的泛型参数以及各种疯狂的东西)。 您可以通过Class.getDeclaredFields
获取字段( Class.getFields
将为您提供公共字段(包括 supertpye 的字段) - 毫无意义)。 要获取基本类型字段,请通过Class.getSuperclass
。 注意检查Field.getModifiers
修饰符 - 您可能不会对静态字段感兴趣。
方法field.getGenericType()
返回对Type
接口的引用。 真正的类型可能是TypeVariable
或GenericArrayType
或ParameterizedType
或Class
的实例,或者我目前不知道的其他东西。
需要不同的方法来检索字段的实际类型。
这是我以 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;
}
public static Type[] getGenericTypes(Field field) {
ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType();
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
return actualTypeArguments;
}
class User{
...
private Set<Authority> authorities = new HashSet<>();
...
}
/// usage
Class c = User.class;
Field field = c.getDeclaredField("authorities");
Type[] types = getGenericTypes(field);
log.info("Types: {}", types);
/// result
Types: class com.fajar.medicalinventory.entity.Authority
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.