代码之家  ›  专栏  ›  技术社区  ›  Ruan Mendes

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

  •  32
  • Ruan Mendes  · 技术社区  · 16 年前

    我一直在尝试确定类中字段的类型。我已经看过了所有的内省方法,但还没有完全弄明白怎么做。这将用于从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 ,而不是 列表 属于

    谢谢

    6 回复  |  直到 9 年前
        1
  •  63
  •   Community Mohan Dere    8 年前

    看看 Obtaining Field Types 来自Java教程 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() 获取参数的原始类型和类型数组的步骤 参数化类型 分别地下面的代码演示了这一点:

    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
    
        2
  •  6
  •   Ruan Mendes    16 年前

    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
    
        3
  •  4
  •   javaBeCool    12 年前

    我还没有找到任何通过继承层确定通用字段类型的框架,因此我编写了一些方法:

    清单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);
    }
    

    我期待着听到你的反馈!

        4
  •  3
  •   dfa    16 年前

    以这个片段为例:

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

    关键类是 Field

        5
  •  2
  •   Reto Höhener    8 年前

      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();
        }
      }
    
        6
  •  1
  •   Tom Hawtin - tackline    16 年前

    正如dfa指出的,您可以使用 java.lang.reflect.Field.getType . 您可以使用 Field.getGenericType (可能有通配符、绑定的泛型参数和各种疯狂)。你可以把田地穿过 Class.getDeclaredFields ( Class.getFields Class.getSuperclass Field.getModifiers -静态字段可能对您不感兴趣。

        7
  •  1
  •   Alexey    5 年前

    方法 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的实例时,方法使用先前获得的映射来恢复该类型:

    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;
    }
    
        8
  •  0
  •   Fajar AM    4 年前
    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