Qual è la differenza tra getFields e getDeclaredFields nella riflessione Java


195

Sono un po 'confuso sulla differenza tra il getFieldsmetodo e il getDeclaredFieldsmetodo quando si utilizza la riflessione Java.

Ho letto che getDeclaredFieldsti dà accesso a tutti i campi della classe e che getFieldsrestituisce solo campi pubblici. Se questo è il caso, perché non lo usi sempre getDeclaredFields?

Qualcuno può approfondire questo, e spiegare la differenza tra i due metodi e quando / perché vorresti usarne uno rispetto all'altro?


3
getFieldpuò ottenere un campo ereditato da una superclasse ma getDeclaredFieldnon può. getDeclaredFieldlimitarsi alla classe su cui si chiama la funzione.
user2336315

@ user2336315 che è corretto, tuttavia getFieldnon è possibile accedere ai membri privati
Madbreaks

Risposte:


258

getFields ()

Tutti i publiccampi nella gerarchia dell'intera classe.

getDeclaredFields ()

Tutti i campi, indipendentemente dalla loro accessibilità ma solo per la classe corrente, non le classi base da cui la classe corrente potrebbe ereditare.

Per ottenere tutti i campi nella gerarchia, ho scritto la seguente funzione:

public static Iterable<Field> getFieldsUpTo(@Nonnull Class<?> startClass, 
                                   @Nullable Class<?> exclusiveParent) {

   List<Field> currentClassFields = Lists.newArrayList(startClass.getDeclaredFields());
   Class<?> parentClass = startClass.getSuperclass();

   if (parentClass != null && 
          (exclusiveParent == null || !(parentClass.equals(exclusiveParent)))) {
     List<Field> parentClassFields = 
         (List<Field>) getFieldsUpTo(parentClass, exclusiveParent);
     currentClassFields.addAll(parentClassFields);
   }

   return currentClassFields;
}

La exclusiveParentclasse viene fornita per impedire il recupero dei campi da Object. Potrebbe essere nullse vuoi i Objectcampi.

Per chiarire, Lists.newArrayListviene dalla Guava.

Aggiornare

Cordiali saluti, il codice sopra è pubblicato su GitHub nel mio progetto LibEx in ReflectionUtils .


8
Ottima risposta, ma va notato che i campi privati ​​nelle superclassi non possono essere utilizzati da istanze della classe corrente per Field#gete metodi simili. In altre parole, questo approccio non consente alla classe corrente di accedere all'interfaccia privata della sua superclasse, allo stesso modo della compilazione tipica.
FThompson,

4
@Vulcan True a meno che il codice non sia stato scritto per utilizzare la reflection per modificare l'ambito tramite setAccessiblee non è presente alcun Security Manager
John B

Leggero nit, dovrebbe essere "(non importa l'accessibilità)" non "(non importa l'ambito)". Tutti i campi hanno lo stesso ambito, vale a dire il corpo della classe .
yshavit,

@yshavit Grazie. Aggiornato.
John B,

1
Non lo sarebbe. Poiché i privatecampi sono accessibili solo tramite il getDeclaredFieldsquale è specifico della classe. Ogni campo (anche con lo stesso tipo e nome) sarebbero Fieldistanze distinte .
John B,

7

Come già accennato, Class.getDeclaredField(String)guarda solo i campi daClass cui lo chiami.

Se vuoi cercare a Fieldnella Classgerarchia, puoi usare questa semplice funzione:

/**
 * Returns the first {@link Field} in the hierarchy for the specified name
 */
public static Field getField(Class<?> clazz, String name) {
    Field field = null;
    while (clazz != null && field == null) {
        try {
            field = clazz.getDeclaredField(name);
        } catch (Exception e) {
        }
        clazz = clazz.getSuperclass();
    }
    return field;
}

Questo è utile per trovare un privatecampo da una superclasse, per esempio. Inoltre, se si desidera modificarne il valore, è possibile utilizzarlo in questo modo:

/**
 * Sets {@code value} to the first {@link Field} in the {@code object} hierarchy, for the specified name
 */
public static void setField(Object object, String fieldName, Object value) throws Exception {
    Field field = getField(object.getClass(), fieldName);
    field.setAccessible(true);
    field.set(object, value);
}

leggera modifica per gettare ancora l'errore se non trovato affattotry try { field = clazz.getDeclaredField(name); } catch (NoSuchFieldException e) { clazz = clazz.getSuperclass(); if(clazz==null){ throw e; } }
Sven Dhaens,

5

public Field[] getFields() throws SecurityException

Restituisce un array contenente oggetti Field che riflettono tutti i campi pubblici accessibili della classe o dell'interfaccia rappresentati da questo oggetto Class. Gli elementi nell'array restituiti non vengono ordinati e non sono in alcun ordine particolare. Questo metodo restituisce una matrice di lunghezza 0 se la classe o l'interfaccia non ha campi pubblici accessibili o se rappresenta una classe di matrice, un tipo primitivo o nulla.

In particolare, se questo oggetto Class rappresenta una classe, questo metodo restituisce i campi pubblici di questa classe e di tutte le sue superclassi. Se questo oggetto Class rappresenta un'interfaccia, questo metodo restituisce i campi di questa interfaccia e di tutte le sue superfacce.

Il campo di lunghezza implicita per la classe di array non si riflette in questo metodo. Il codice utente deve utilizzare i metodi della classe Array per manipolare le matrici.


public Field[] getDeclaredFields() throws SecurityException

Restituisce una matrice di oggetti Field che riflette tutti i campi dichiarati dalla classe o dall'interfaccia rappresentata da questo oggetto Class. Ciò include l'accesso pubblico, protetto, predefinito (pacchetto) e privato , ma esclude i campi ereditati . Gli elementi nell'array restituiti non vengono ordinati e non sono in alcun ordine particolare. Questo metodo restituisce un array di lunghezza 0 se la classe o l'interfaccia non dichiara campi o se questo oggetto Class rappresenta un tipo primitivo, una classe di array o un vuoto.


E se avessi bisogno di tutti i campi di tutte le classi genitore? È necessario un po 'di codice, ad es. Da https://stackoverflow.com/a/35103361/755804 :

public static List<Field> getAllModelFields(Class aClass) {
    List<Field> fields = new ArrayList<>();
    do {
        Collections.addAll(fields, aClass.getDeclaredFields());
        aClass = aClass.getSuperclass();
    } while (aClass != null);
    return fields;
}

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.