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

42

我一直在试图确定一个类中字段的类型。我已经看过所有的反射方法,但还没有完全搞清楚如何做到这一点。这将用于从Java类生成xml/json。我已经看了很多这里的问题,但还没有找到我需要的。

示例:

class Person {
    public final String name;
    public final List<Person> children;
}

当我序列化这个对象时,我需要知道children字段是一个类型为Person的对象列表,以便我可以正确地序列化它。

我尝试过:

for (Field field : Person.class.getDeclaredFields()) {
    System.out.format("Type: %s%n", field.getType());
}

但这只会告诉我它是一个List,而不是由Person组成的List

谢谢


1
List<Person>?除非我误解了重点。 - Woot4Moo
8个回答

67

请查看Java教程《获取字段类型》,它来自于Java教程的Reflex 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

5
Pascal,你已经非常接近了。你需要使用以下代码获取类型:Type type = Field.getGenericType();然后检查它是否为 ParameterizedType,并且获取参数。这会进入一个兔子洞,提问者需要定义限制或准备编写一些相当复杂的代码。 - wowest
1
@Juan Mendes,您应该在每个答案左侧看到一个勾选图标,位于投票数字旁边,只需单击属于正确答案的那个选项即可将其变为绿色。 - Iker Jimenez
我知道我可以将这个答案标记为正确答案。但直到Pascal根据wowest的评论进行了编辑,它才变得正确。无论如何,我列出的示例包含我所需要的所有信息,但在这么多人帮助我的情况下,我不想把自己的答案标记为正确答案。 Pascal的示例仍然缺少关键方法(ParameterizedType.getRawType和ParameterizedType.getActualArguments)。Pascal,您能否编辑示例以显示使用这两种方法? - Ruan Mendes
@Juan,一开始我并不想提供所有的代码,因为你已经完成了工作并完全回答了自己的问题。但是由于请求得如此友好,我更新了我的答案(当然,与你写的非常相似)。 - Pascal Thivent
@Pascal,这是我第一次在Stack Overflow上发布问题,发现这里的人们在提问和回答时都非常有礼貌和聪明。我使用过的其他论坛通常只有其中之一:)。 Stack Overflow肯定会成为我提问的首选地方,我也一定会尽力回答问题。 - Ruan Mendes
显示剩余3条评论

6
这里有一个例子回答了我的问题。
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());
  }
}

这个输出的结果为:

字段名称:name
- 字段类型:class java.lang.String
字段名称:children
- 原始类型:interface java.util.List
- 类型参数:class com.blah.Person

5

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

这个逻辑通过字段信息和当前对象类来确定类型。

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

我期待收到您的反馈!


谢谢!这个方法帮了很大的忙。我建议添加一个检查,检查type.type是否是ParameterizedTypeImpl的实例,在这种情况下,.getRawType()方法会产生类型。这将在类型为List<Double>时获取List。(否则可能会得到“ParameterizedTypeImpl不能转换为java.lang.Class”的错误) - Václav Blažej
非常棒,还提供了与这个代码示例配套的测试。非常感谢。 - undefined

3

请看这段代码:

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

关键类是Field



这只是公共字段(包括静态字段),使用Class.getFields获得。同时,它将是擦除类型。 - Tom Hawtin - tackline
那我误解了问题。 - dfa

3
这是我的看法。它不能处理每种可能的情况(肯定有一些错误),但它确实处理了我代码中出现的每种情况。这包括这些声明,对于许多用例来说应该是一个很好的起点:
  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();
    }
  }

你能否评论一下你的程序相比javaBeCool的有什么优势?我想知道是否需要结合两者来处理递归泛型和继承。请参见https://dev59.com/CHI-5IYBdhLWcg3wZ3nC#19363555。 - Ruan Mendes
1
@JuanMendes javaBeCool 似乎涉及不同的用例。我的回答是关于将字段声明作为字符串获取,我在一个代码生成项目中使用它。我的原始问题被标记为这个问题的重复 - 这就是我在这里发布的原因 (https://dev59.com/KKPia4cB1Zd3GeqPx2fJ)。 - Reto Höhener

1

正如 dfa 所指出的那样,你可以使用 java.lang.reflect.Field.getType 来获取被擦除的类型。你可以使用 Field.getGenericType 来获取泛型类型(可能包含通配符和绑定的泛型参数以及各种疯狂的东西)。通过 Class.getDeclaredFields 可以获取字段(Class.getFields 将给你公共字段(包括超类的字段)- 没有意义)。要获取基本类型的字段,请通过 Class.getSuperclass 进行操作。注意检查来自 Field.getModifiers 的修饰符 - 对你来说静态字段可能并不感兴趣。


1

field.getGenericType() 方法返回一个 Type 接口的引用。 实际类型可能是 TypeVariableGenericArrayTypeParameterizedTypeClass,或者其他我目前不知道的类型。

需要采用不同的方法来获取字段的实际类型。

以下是我获取公共字段信息的解决方案,以 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;
}

0
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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接