¿Cómo puedo determinar el tipo de campo genérico en Java?

He estado tratando de determinar el tipo de campo en una clase. He visto todos los métodos de introspección pero aún no he descubierto cómo hacerlo. Esto se usará para generar xml / json de una clase java. He visto varias de las preguntas aquí, pero no he encontrado exactamente lo que necesito.

Ejemplo:

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

Cuando marque este objeto, necesito saber que el campo de chidren es una lista de objetos de tipo Person , por lo que puedo ordenarlos correctamente.

Había intentado

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

Pero esto solo me dirá que es una List , no una List de Person

Gracias

Eche un vistazo a la Obtención de tipos de campo del Tutorial de Java Trail: The Reflection API .

Básicamente, lo que tienes que hacer es obtener todo java.lang.reflect.Field de tu clase y llamar a Field#getType() en cada uno de ellos (verifique la edición a continuación). Para obtener todos los campos de objetos, incluidos los campos públicos, protegidos, de paquetes y de acceso privado, simplemente use Class.getDeclaredFields() . Algo como esto:

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

EDITAR: Como señaló wowest en un comentario, realmente necesita llamar al Field#getGenericType() , verificar si el Type devuelto es un tipo ParameterizedType y luego tomar los parámetros en consecuencia. Use ParameterizedType#getRawType() y ParameterizedType#getActualTypeArgument() para obtener el tipo sin ParameterizedType#getActualTypeArgument() y una matriz del argumento de tipos de ParameterizedType respectivamente. El siguiente código demuestra esto:

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

Y saldría:

 Field: name - Type: class java.lang.String Field: children - Raw type: interface java.util.List - Type args: class foo.Person 

Aquí hay un ejemplo que responde mi pregunta

 class Person { public final String name; public final List 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()); } } 

Estas salidas

 nombre del campo: nombre
 - tipo de campo: clase java.lang.String
 nombre del campo: niños
 -raw tipo: interfaz java.util.List
 -type arg: clase com.blah.Person

No he encontrado ningún marco que determine un tipo de campo genérico a través de las capas de herencia, así que he escrito algún método:

Esta lógica determina el tipo a través de la información de campo y la clase de objeto actual.

Listado 1 – lógica:

 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; } 

Listado 2 – Muestras / Pruebas:

 class GenericSuperClass { T t; E e; A a; BigDecimal b; } class GenericDefinition extends GenericSuperClass { } @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 extends GenericSuperClass { } // T = Integer, E = String, A = Double class SimpleTopClass extends MiddleClass { } @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 extends MiddleClass { } // T = Integer, E = Double, A = Float class ComplexTopClass extends TopMiddleClass {} @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 extends MiddleClass {} // T = Integer, E = Double, A = Float ; this class should map between a and e class TopConfusingClass extends ConfusingClass {} @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); } 

¡Espero escuchar tus comentarios!

tomar este fragmento:

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

la clase clave es Field

Como señala dfa, puede obtener el tipo borrado con java.lang.reflect.Field.getType . Puede obtener el tipo genérico con Field.getGenericType (que puede tener comodines y parámetros generics vinculados y todo tipo de locura). Puede obtener los campos a través de Class.getDeclaredFields ( Class.getFields le dará campos públicos (incluidos los de la supertpye) – inútil). Para obtener los campos de tipo de base, vaya a Class.getSuperclass . Nota para comprobar los modificadores de Field.getModifiers : los campos estáticos probablemente no te interesen.

Aquí está mi opinión. No puede manejar todos los casos posibles (y seguramente tiene algunos errores), pero maneja todos los casos que ocurren en mi código hasta el momento. Eso incluye estas declaraciones, que deberían ser un buen comienzo para muchos casos de uso:

  private int primitiveField1; private Object field1; private List field2; private Map field3; private Map< ? extends String, List, Object>>> field4; private char[] array1; private Character[] array2; private Class< ? extends Integer>[] array3; private List[] array4; private InnerClass innerClass; 

Implementación:

  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 { // eg WildcardTypeImpl (Class< ? extends Integer>) return genericType.getTypeName(); } }