Come posso determinare il tipo di un campo generico in Java?

Ho cercato di determinare il tipo di un campo in una class. Ho visto tutti i metodi di introspezione ma non ho ancora capito come farlo. Questo verrà utilizzato per generare xml / json da una class java. Ho esaminato una serie di domande qui ma non ho trovato esattamente quello di cui ho bisogno.

Esempio:

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

Quando eseguo il marshalling di questo object, ho bisogno di sapere che il campo chidren è un elenco di oggetti di tipo Person , quindi posso eseguirlo correttamente.

Ci avevo provato

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

Ma questo mi dirà solo che si tratta di una List , non di una List di Person

Grazie

Dai un’occhiata a Ottenere i tipi di campo da Java Tutorial Trail: l’API Reflection .

Fondamentalmente, ciò che devi fare è ottenere tutto java.lang.reflect.Field della tua class e chiamare Field#getType() su ognuno di essi (controlla la modifica sotto). Per ottenere tutti i campi object compresi i campi pubblico, protetto, di pacchetto e di accesso privato, è sufficiente utilizzare Class.getDeclaredFields() . Qualcosa come questo:

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

EDIT: Come sottolineato da wowest in un commento, in realtà è necessario chiamare Field#getGenericType() , verificare se il Type restituito è un ParameterizedType e quindi acquisire i parametri di conseguenza. Utilizzare ParameterizedType#getRawType() e ParameterizedType#getActualTypeArgument() per ottenere rispettivamente il tipo raw e un array dell’argomento types di ParameterizedType . Il seguente codice dimostra questo:

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

E produrrebbe:

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

Ecco un esempio che risponde alla mia domanda

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

Questo produce

 nome del campo: nome
 -field type: class java.lang.String
 nome del campo: bambini
 -raw type: interface java.util.List
 -tipo arg: class com.blah.Person

Non ho trovato nessun framework che determini un tipo di campo generico attraverso i livelli di ereditarietà, quindi ho scritto un metodo:

Questa logica determina il tipo attraverso le informazioni sul campo e la class dell’object corrente.

Listato 1 – logica:

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

Listato 2 – Esempi / test:

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

Non vedo l’ora di sentire il tuo feedback!

prendi questo snippet:

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

la class chiave è Field

Come indicato da dfa, puoi ottenere il tipo cancellato con java.lang.reflect.Field.getType . È ansible ottenere il tipo generico con Field.getGenericType (che potrebbe contenere caratteri jolly e parametri generici associati e ogni sorta di follia). Puoi ottenere i campi attraverso Class.getDeclaredFields ( Class.getFields ti darà campi pubblici (compresi quelli del supertpye) – senza senso). Per ottenere i campi del tipo di base, passare a Class.getSuperclass . Nota per controllare i modificatori da Field.getModifiers – i campi statici probabilmente non saranno interessanti per te.

Ecco la mia versione. Non può gestire ogni caso ansible (e sicuramente ha alcuni bug), ma gestisce ogni caso che si verifica nel mio codice fino ad ora. Ciò include queste dichiarazioni, che dovrebbero essere un buon inizio per molti casi d’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; 

Implementazione:

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