2009-12-08 10 views
29

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

Esempio:

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

Quando ho marshall questo oggetto, ho bisogno di sapere che il campo chidren è una lista di oggetti di tipo Person, così posso marshall in modo corretto.

avevo provato

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

Ma questo sarà solo mi dicono che si tratta di un List, non un List di Person s

Grazie

+1

Lista ? A meno che non mi sfugga il punto. – Woot4Moo

+0

Questo è ciò che intendevo –

risposta

50

Dai un'occhiata alla Obtaining Field Types dal Java Tutorial Trail: The Reflection API .

In sostanza, ciò che è necessario fare è quello di ottenere tutti java.lang.reflect.Field della classe e chiamare Field#getType() su ciascuno di loro (check modifica sotto). Per ottenere tutti i campi oggetto compresi i campi pubblici, protetti, di pacchetto e di accesso privato, è sufficiente utilizzare Class.getDeclaredFields(). Qualcosa di simile a 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, hai veramente bisogno di chiamare Field#getGenericType(), verificare se la tornata Type è un ParameterizedType e poi prendere i parametri di conseguenza. Utilizzare ParameterizedType#getRawType() e ParameterizedType#getActualTypeArgument() per ottenere rispettivamente il tipo non elaborato e un array dell'argomento tipi di uno ParameterizedType. Il codice seguente illustra 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 sarebbe uscita:

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

Pascal - sei super vicino. vuoi type type = Field.getGenericType(); E quindi controllare se è un ParameterizedType, quindi prendere i parametri. Questo finisce per essere una tana di coniglio - chi chiede deve definire dei limiti o essere pronto a scrivere un codice piuttosto elaborato. – wowest

+0

Grazie a tutti, questo è esattamente ciò di cui ho bisogno in combinazione con ParameterizedType.getRawType e ParameterizedType.getActualArguments. –

+0

Non riesco a capire come contrassegnare il tuo commento come la risposta giusta quindi risponderò alla mia stessa domanda in modo da poter pubblicare un esempio –

3

prendere questo frammento:

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

classe chiave è Field

+0

Questi sono solo campi pubblici (incluso statico) con 'Class.getFields'. Inoltre sarà il tipo cancellato. –

+0

poi ho frainteso la domanda – dfa

1

Come DFA fa notare, è può ottenere il tipo cancellato con java.lang.reflect.Field.getType. È possibile ottenere il tipo generico con Field.getGenericType (che potrebbe avere caratteri jolly e parametri generici associati e tutti i tipi di follia). È possibile ottenere i campi attraverso Class.getDeclaredFields (Class.getFields vi darà campi pubblici (compresi quelli del supertpye) - senza senso). Per ottenere i campi del tipo di base, passare a Class.getSuperclass. Nota per verificare i modificatori da Field.getModifiers - i campi statici probabilmente non saranno interessanti per te.

4

Ecco un esempio che risponde alla mia domanda

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

Emette

 
field name: name 
-field type: class java.lang.String 
field name: children 
-raw type:interface java.util.List 
-type arg: class com.blah.Person 
+0

Downvoter, se spieghi il downvote posso migliorare la risposta. –

4

non ho trovato alcun quadro che determina un tipo di campo generico attraverso gli strati di successione in modo che io ho scritto un po ' metodo:

Questa logica determina il tipo tramite le informazioni sul campo e la classe dell'oggetto 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; 
} 

Listing 2 - Campioni/Test:

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

io vedo l'ora di sentire i vostri commenti!

+0

Grazie! Questo metodo ha aiutato molto. Consiglierei l'aggiunta di controllare se type.type è un'istanza di 'ParameterizedTypeImpl', nel qual caso il metodo' .getRawType() 'produce il tipo. Questo preleverà 'List' quando type è' List '. (Altrimenti si potrebbe ottenere 'ParameterizedTypeImpl non può essere lanciato a java.lang.Class') –

1

Ecco la mia opinione. Non può gestire ogni caso possibile (e sicuramente ha alcuni bug), ma gestisce ogni caso che si verifica nel mio codice fino ad ora. Che include queste dichiarazioni, che dovrebbe essere un buon inizio per molti casi di utilizzo:

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; 

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

Puoi commentare ciò che la vostra può farlo javaBeCool di no? Mi chiedo se un mix di entrambi sia ciò che è necessario per gestire i generici ricorsivi più l'ereditarietà. Vedere https://stackoverflow.com/a/19363555/227299 –

+1

@JuanMendes javaBeCool sembra indirizzare un diverso caso d'uso. La mia risposta è di ottenere la dichiarazione del campo come una stringa, la uso in un progetto di generazione del codice. La mia domanda originale è stata contrassegnata come duplicata di questa domanda: è per questo che ho postato qui (https://stackoverflow.com/q/45083186/1124509). – Zalumon

Problemi correlati