我一直在尝试确定类中字段的类型。我已经看到了所有的内省方法,但还没有弄清楚如何做到这一点。这将用于从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
Person
由于
答案 0 :(得分:53)
从Java教程Obtaining Field Types查看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
是否为{{1}然后相应地获取参数。使用ParameterizedType#getRawType()
和ParameterizedType#getActualTypeArgument()
分别获取原始类型和ParameterizedType
的types参数数组。以下代码演示了这一点:
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());
}
}
答案 1 :(得分:5)
这是一个回答我的问题的例子
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
答案 2 :(得分:4)
我没有找到任何通过继承层确定通用字段类型的框架,所以我写了一些方法:
此逻辑通过字段信息和当前对象类确定类型。
清单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);
}
我期待听到您的反馈!
答案 3 :(得分:3)
拿这个片段:
for (Field field : Person.class.getFields()) {
System.out.println(field.getType());
}
关键类是Field
答案 4 :(得分:2)
这是我的看法。它无法处理所有可能的情况(肯定有一些错误),但它确实处理了我的代码到目前为止发生的每一种情况。这包括这些声明,这应该是许多用例的良好开端:
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();
}
}
答案 5 :(得分:1)
正如dfa指出的那样,您可以使用java.lang.reflect.Field.getType
获取已删除的类型。您可以使用Field.getGenericType
获取泛型类型(可能具有通配符和绑定的泛型参数以及各种疯狂)。您可以通过Class.getDeclaredFields
获取字段(Class.getFields
将为您提供公共字段(包括supertpye的字段) - 毫无意义)。要获取基本类型字段,请浏览Class.getSuperclass
。请注意检查Field.getModifiers
中的修饰符 - 静态字段可能对您不感兴趣。
答案 6 :(得分:1)
方法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;
}
答案 7 :(得分: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