Was ist der Unterschied zwischen getFields und getDeclaredFields in der Java-Reflektion?

194

Ich bin etwas verwirrt über den Unterschied zwischen der getFieldsMethode und der getDeclaredFieldsMethode bei der Verwendung von Java Reflection.

Ich habe gelesen, dass getDeclaredFieldsSie Zugriff auf alle Felder der Klasse haben und getFieldsnur öffentliche Felder zurückgeben. Wenn dies der Fall ist, warum würden Sie es nicht immer verwenden getDeclaredFields?

Kann jemand bitte näher darauf eingehen und den Unterschied zwischen den beiden Methoden erklären und wann / warum Sie eine über die andere verwenden möchten?

BlackHatSamurai
quelle
3
getFieldkann ein Feld von einer Oberklasse erben, getDeclaredFieldkann es aber nicht. getDeclaredFieldBeschränken Sie sich auf die Klasse, für die Sie die Funktion aufrufen.
user2336315
@ user2336315 das ist korrekt, getFieldkann aber nicht auf private Mitglieder zugreifen
Madbreaks

Antworten:

258

getFields ()

Alle publicFelder bilden die gesamte Klassenhierarchie.

getDeclaredFields ()

Alle Felder, unabhängig von ihrer Zugänglichkeit, jedoch nur für die aktuelle Klasse, keine Basisklassen, von denen die aktuelle Klasse möglicherweise erbt.

Um alle Felder in der Hierarchie nach oben zu bringen, habe ich die folgende Funktion geschrieben:

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

Die exclusiveParentKlasse wird bereitgestellt, um das Abrufen von Feldern aus zu verhindern Object. Es kann sein, nullwenn Sie die ObjectFelder wollen .

Zur Verdeutlichung Lists.newArrayListkommt aus Guave.

Aktualisieren

Zu Ihrer Information , der obige Code wird auf GitHub in meinem LibEx- Projekt in ReflectionUtils veröffentlicht .

John B.
quelle
8
Gute Antwort, aber es sollte beachtet werden, dass private Felder in Oberklassen nicht von Instanzen der aktuellen Klasse für Field#getund ähnliche Methoden verwendet werden können. Mit anderen Worten, dieser Ansatz ermöglicht der aktuellen Klasse keinen Zugriff auf die private Schnittstelle ihrer Oberklasse, wie dies bei der typischen Kompilierung nicht der Fall ist.
FThompson
4
@ Vulcan True, es sei denn, der Code ist so geschrieben, dass der Bereich mithilfe von Reflection geändert wird, setAccessibleund es ist kein Sicherheitsmanager vorhanden
John B
Leichte Nit, sollte "(unabhängig von der Zugänglichkeit)" nicht "(unabhängig vom Umfang)" sein. Alle Felder haben den gleichen Umfang, nämlich den Körper der Klasse .
Yshavit
@yshavit Danke. Aktualisiert.
John B
1
Es würde nicht. Da auf privateFelder nur zugegriffen werden kann getDeclaredFields, ist dies klassenspezifisch. Jedes Feld (auch mit demselben Typ und Namen) wäre eine unterschiedliche FieldInstanz.
John B
7

Wie bereits erwähnt, werden Class.getDeclaredField(String)nur die Felder angezeigt, Classin denen Sie sie aufrufen.

Wenn Sie Fieldin der ClassHierarchie nach suchen möchten , können Sie diese einfache Funktion verwenden:

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

Dies ist beispielsweise nützlich, um ein privateFeld aus einer Oberklasse zu finden . Wenn Sie den Wert ändern möchten, können Sie ihn auch folgendermaßen verwenden:

/**
 * 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);
}
IvanRF
quelle
leichte Modifikation, um immer noch Fehler zu werfen, wenn überhaupt nicht gefundentry try { field = clazz.getDeclaredField(name); } catch (NoSuchFieldException e) { clazz = clazz.getSuperclass(); if(clazz==null){ throw e; } }
Sven Dhaens
5

public Field[] getFields() throws SecurityException

Gibt ein Array zurück, das Feldobjekte enthält, die alle zugänglichen öffentlichen Felder der Klasse oder Schnittstelle widerspiegeln, die von diesem Klassenobjekt dargestellt werden. Die zurückgegebenen Elemente im Array sind nicht sortiert und befinden sich nicht in einer bestimmten Reihenfolge. Diese Methode gibt ein Array der Länge 0 zurück, wenn die Klasse oder Schnittstelle keine zugänglichen öffentlichen Felder hat oder wenn es eine Array-Klasse, einen primitiven Typ oder eine Leere darstellt.

Wenn dieses Class-Objekt eine Klasse darstellt, gibt diese Methode die öffentlichen Felder dieser Klasse und aller ihrer Oberklassen zurück. Wenn dieses Class-Objekt eine Schnittstelle darstellt, gibt diese Methode die Felder dieser Schnittstelle und aller ihrer Superschnittstellen zurück.

Das implizite Längenfeld für die Array-Klasse wird von dieser Methode nicht berücksichtigt. Benutzercode sollte die Methoden der Klasse Array verwenden, um Arrays zu bearbeiten.


public Field[] getDeclaredFields() throws SecurityException

Gibt ein Array von Feldobjekten zurück, die alle Felder widerspiegeln, die von der Klasse oder Schnittstelle deklariert wurden, die von diesem Klassenobjekt dargestellt wird. Dies umfasst öffentliche, geschützte, Standardzugriffe (Paketzugriffe) und private Felder, schließt jedoch geerbte Felder aus. Die zurückgegebenen Elemente im Array sind nicht sortiert und befinden sich nicht in einer bestimmten Reihenfolge. Diese Methode gibt ein Array der Länge 0 zurück, wenn die Klasse oder Schnittstelle keine Felder deklariert oder wenn dieses Class-Objekt einen primitiven Typ, eine Array-Klasse oder void darstellt.


Und was ist, wenn ich alle Felder aus allen übergeordneten Klassen benötige? Es wird Code benötigt, z. B. von 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;
}
18446744073709551615
quelle