如何确定java中泛型字段的类型?

zu0ti5jz  于 2021-07-12  发布在  Java
关注(0)|答案(8)|浏览(269)

我一直在尝试确定类中字段的类型。我看过所有的自省方法,但还没有完全弄清楚怎么做。这将用于从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 ,不是 ListPerson s
谢谢

dwthyt8l

dwthyt8l1#

看看从java教程trail:reflectionapi中获取字段类型。
基本上,你需要做的是 java.lang.reflect.Field 你的班级和电话 Field#getType() 在每个页面上(选中下面的“编辑”)。要获取所有对象字段,包括public、protected、package和private访问字段,只需使用 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
rsl1atfo

rsl1atfo2#

下面的例子回答了我的问题

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
rqdpfwrv

rqdpfwrv3#

我还没有找到任何通过继承层确定泛型字段类型的框架,所以我编写了一些方法:
此逻辑通过字段信息和当前对象类确定类型。
清单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);
}

我期待着听到你的反馈!

zaqlnxep

zaqlnxep4#

以这个片段为例:

for (Field field : Person.class.getFields()) {
        System.out.println(field.getType());
 }

关键类是field

bd1hkmkf

bd1hkmkf5#

这是我的照片。它不能处理所有可能的情况(当然也有一些bug),但它确实处理了迄今为止在我的代码中发生的所有情况。这包括这些声明,对于许多用例来说,这应该是一个良好的开端:

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();
    }
  }
u4vypkhs

u4vypkhs6#

正如dfa指出的,您可以使用 java.lang.reflect.Field.getType . 您可以使用 Field.getGenericType (可能有通配符和绑定的泛型参数以及各种各样的疯狂)。你可以把田地弄过去 Class.getDeclaredFields ( Class.getFields 将为您提供公共字段(包括supertpye的字段)-无意义。要获取基本类型字段,请通过 Class.getSuperclass . 检查修饰符的注意事项 Field.getModifiers -静态字段可能对您不感兴趣。

gxwragnw

gxwragnw7#

方法 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的示例时,方法使用前面获得的Map来还原该泛型参数的实际类型:

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, MyClass, 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;
}
q5lcpyga

q5lcpyga8#

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

相关问题