Abrufen der geerbten Attributnamen / -werte mit Java Reflection

128

Ich habe ein Java-Objekt 'ChildObj', das von 'ParentObj' erweitert wurde. Wenn es nun möglich ist, alle Attributnamen und -werte von ChildObj, einschließlich der geerbten Attribute, mithilfe des Java-Reflexionsmechanismus abzurufen?

Class.getFields gibt mir das Array öffentlicher Attribute und Class.getDeclaredFields gibt mir das Array aller Felder, aber keines von ihnen enthält die Liste der geerbten Felder.

Gibt es eine Möglichkeit, auch die geerbten Attribute abzurufen?

Veera
quelle

Antworten:

173

Nein, du musst es selbst schreiben. Es ist eine einfache rekursive Methode, die in Class.getSuperClass () aufgerufen wird :

public static List<Field> getAllFields(List<Field> fields, Class<?> type) {
    fields.addAll(Arrays.asList(type.getDeclaredFields()));

    if (type.getSuperclass() != null) {
        getAllFields(fields, type.getSuperclass());
    }

    return fields;
}

@Test
public void getLinkedListFields() {
    System.out.println(getAllFields(new LinkedList<Field>(), LinkedList.class));
}
dfa
quelle
2
Ja. dachte darüber nach. wollte aber überprüfen, ob es einen anderen Weg gibt, das zu tun. Vielen Dank. :)
Veera
7
Ein veränderliches Argument weiterzugeben und zurückzugeben, ist wahrscheinlich kein großartiges Design. fields.addAll (type.getDeclaredFields ()); wäre konventioneller als eine erweiterte for-Schleife mit add.
Tom Hawtin - Tackline
Ich hätte das Bedürfnis, es zumindest zu kompilieren (bei Stackoverflow!) Und wahrscheinlich eine kleine Arrays.asList hinzuzufügen.
Tom Hawtin - Tackline
Es scheint, dass Ihr Code alle Felder sammelt, auch private und statische Felder, die nicht vererbt werden.
Peter Verhas
90
    public static List<Field> getAllFields(Class<?> type) {
        List<Field> fields = new ArrayList<Field>();
        for (Class<?> c = type; c != null; c = c.getSuperclass()) {
            fields.addAll(Arrays.asList(c.getDeclaredFields()));
        }
        return fields;
    }
Esko Luontola
quelle
9
Dies ist meine bevorzugte Lösung, ich würde sie jedoch "getAllFields" nennen, da sie auch die Felder der angegebenen Klasse zurückgibt.
Pino
3
Obwohl ich Rekursivität sehr mag (es macht Spaß!), Bevorzuge ich die Lesbarkeit dieser Methode und die intuitiveren Parameter (keine neue Sammlung muss übergeben werden), nicht mehr if (implizit in der for-Klausel) und keine Iteration über Felder sich.
Remi Morin
es zeigt, dass rekursiv unnötig ist und .. Ich mag Funktionscodes! Vielen Dank! :)
Wassermann Power
In vielen Jahren denke ich immer, dass der Anfangswert für nur eine ganze Zahl ist. Mit @ Veeras Frage kann es meiner Meinung nach nur durch Rekursiv gelöst werden. @ Esko Luontola Ihr Befehl ist fantastisch.
Touya Akira
@Esko: Vielen Dank. Den Tag gerettet! Es ist prägnant und funktioniert einwandfrei!
Gaurav
37

Wenn Sie sich stattdessen auf eine Bibliothek verlassen möchten, bietet Apache Commons Lang Version 3.2+ Folgendes FieldUtils.getAllFieldsList:

import java.lang.reflect.Field;
import java.util.AbstractCollection;
import java.util.AbstractList;
import java.util.AbstractSequentialList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.lang3.reflect.FieldUtils;
import org.junit.Assert;
import org.junit.Test;

public class FieldUtilsTest {

    @Test
    public void testGetAllFieldsList() {

        // Get all fields in this class and all of its parents
        final List<Field> allFields = FieldUtils.getAllFieldsList(LinkedList.class);

        // Get the fields form each individual class in the type's hierarchy
        final List<Field> allFieldsClass = Arrays.asList(LinkedList.class.getFields());
        final List<Field> allFieldsParent = Arrays.asList(AbstractSequentialList.class.getFields());
        final List<Field> allFieldsParentsParent = Arrays.asList(AbstractList.class.getFields());
        final List<Field> allFieldsParentsParentsParent = Arrays.asList(AbstractCollection.class.getFields());

        // Test that `getAllFieldsList` did truly get all of the fields of the the class and all its parents 
        Assert.assertTrue(allFields.containsAll(allFieldsClass));
        Assert.assertTrue(allFields.containsAll(allFieldsParent));
        Assert.assertTrue(allFields.containsAll(allFieldsParentsParent));
        Assert.assertTrue(allFields.containsAll(allFieldsParentsParentsParent));
    }
}
Chris
quelle
6
Boom! Ich liebe es, das Rad nicht neu zu erfinden. Prost dafür.
Joshua Pinter
6

Sie müssen anrufen:

Class.getSuperclass().getDeclaredFields()

Reverben der Vererbungshierarchie nach Bedarf.

Nick Holt
quelle
5

Reflections-Bibliothek verwenden:

public Set<Field> getAllFields(Class<?> aClass) {
    return org.reflections.ReflectionUtils.getAllFields(aClass);
}
Lukasz Ochmanski
quelle
4

Die rekursiven Lösungen sind in Ordnung. Das einzige kleine Problem ist, dass sie eine Obermenge deklarierter und geerbter Mitglieder zurückgeben. Beachten Sie, dass die Methode getDeclaredFields () auch private Methoden zurückgibt. Wenn Sie also durch die gesamte Hierarchie der Oberklassen navigieren, werden alle in den Oberklassen deklarierten privaten Felder eingeschlossen, und diese werden nicht vererbt.

Ein einfacher Filter mit einem Modifier.isPublic || Das Prädikat Modifier.isProtected würde Folgendes bewirken:

import static java.lang.reflect.Modifier.isPublic;
import static java.lang.reflect.Modifier.isProtected;

(...)

List<Field> inheritableFields = new ArrayList<Field>();
for (Field field : type.getDeclaredFields()) {
    if (isProtected(field.getModifiers()) || isPublic(field.getModifiers())) {
       inheritableFields.add(field);
    }
}
Marek Dez.
quelle
2
private static void addDeclaredAndInheritedFields(Class<?> c, Collection<Field> fields) {
    fields.addAll(Arrays.asList(c.getDeclaredFields())); 
    Class<?> superClass = c.getSuperclass(); 
    if (superClass != null) { 
        addDeclaredAndInheritedFields(superClass, fields); 
    }       
}

Arbeitsversion der obigen Lösung "DidYouMeanThatTomHa ..."

Theo Platt
quelle
2

Mit der spring util-Bibliothek können Sie überprüfen, ob ein bestimmtes Attribut in der Klasse vorhanden ist:

Field field = ReflectionUtils.findRequiredField(YOUR_CLASS.class, "ATTRIBUTE_NAME");

log.info(field2.getName());

API-Dokument:
https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/util/ReflectionUtils.html

oder

 Field field2 = ReflectionUtils.findField(YOUR_CLASS.class, "ATTRIBUTE_NAME");

 log.info(field2.getName());

API-Dokument:
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/util/ReflectionUtils.html

@Prost

Marcelo Rebouças
quelle
1

Du kannst es versuchen:

   Class parentClass = getClass().getSuperclass();
   if (parentClass != null) {
      parentClass.getDeclaredFields();
   }
Manuel Selva
quelle
1

Kürzer und mit weniger instanziiertem Objekt? ^^

private static Field[] getAllFields(Class<?> type) {
    if (type.getSuperclass() != null) {
        return (Field[]) ArrayUtils.addAll(getAllFields(type.getSuperclass()), type.getDeclaredFields());
    }
    return type.getDeclaredFields();
}
Alexis LEGROS
quelle
HI @Alexis LEGROS: ArrayUtils kann kein Symbol finden.
Touya Akira
1
Diese Klasse stammt von Apache Commons Lang.
Alexis LEGROS
Apache verfügt bereits über eine FieldUtils.getAllFields-Funktion, um diese Fragenanforderung zu verarbeiten.
Touya Akira
1

getFields (): Ruft alle öffentlichen Felder in der gesamten Klassenhierarchie ab und
getDeclaredFields (): Ruft alle Felder ab, unabhängig von ihren Modifikatoren, jedoch nur für die aktuelle Klasse. Sie müssen also für alle beteiligten Hierarchien sorgen.
Ich habe diesen Code kürzlich von org.apache.commons.lang3.reflect.FieldUtils gesehen

public static List<Field> getAllFieldsList(final Class<?> cls) {
        Validate.isTrue(cls != null, "The class must not be null");
        final List<Field> allFields = new ArrayList<>();
        Class<?> currentClass = cls;
        while (currentClass != null) {
            final Field[] declaredFields = currentClass.getDeclaredFields();
            Collections.addAll(allFields, declaredFields);
            currentClass = currentClass.getSuperclass();
        }
        return allFields;
}
Ein Mann
quelle
0
private static void addDeclaredAndInheritedFields(Class c, Collection<Field> fields) {
    fields.addAll(Arrays.asList(c.getDeclaredFields()));
    Class superClass = c.getSuperclass();
    if (superClass != null) {
        addDeclaredAndInheritedFields(superClass, fields);
    }
}
DidYouMeanThatTomHawtin
quelle
0

Dies ist eine Umformulierung der akzeptierten Antwort von @ user1079877. Es kann sein, dass eine Version, die einen Parameter der Funktion nicht ändert und auch einige moderne Java-Funktionen verwendet.

public <T> Field[] getFields(final Class<T> type, final Field... fields) {
    final Field[] items = Stream.of(type.getDeclaredFields(), fields).flatMap(Stream::of).toArray(Field[]::new);
    if (type.getSuperclass() == null) {
        return items;
    } else {
        return getFields(type.getSuperclass(), items);
    }
}

Diese Implementierung macht den Aufruf auch etwas präziser:

var fields = getFields(MyType.class);
prüfend
quelle
0

Es gibt ein paar Macken, die von FieldUtils nicht angesprochen werden - insbesondere synthetische Felder (z. B. von JaCoCo injiziert) und auch die Tatsache, dass ein Aufzählungstyp natürlich ein Feld für jede Instanz hat, und wenn Sie ein Objektdiagramm durchlaufen, erhalten Sie Alle Felder und dann die Felder von jedem von ihnen usw. erhalten, dann werden Sie in eine Endlosschleife geraten, wenn Sie eine Aufzählung treffen. Eine erweiterte Lösung (und um ehrlich zu sein, ich bin sicher, dass dies irgendwo in einer Bibliothek leben muss!) Wäre:

/**
 * Return a list containing all declared fields and all inherited fields for the given input
 * (but avoiding any quirky enum fields and tool injected fields).
 */
public List<Field> getAllFields(Object input) {
    return getFieldsAndInheritedFields(new ArrayList<>(), input.getClass());
}

private List<Field> getFieldsAndInheritedFields(List<Field> fields, Class<?> inputType) {
    fields.addAll(getFilteredDeclaredFields(inputType));
    return inputType.getSuperclass() == null ? fields : getFieldsAndInheritedFields(fields, inputType.getSuperclass());

}

/**
 * Where the input is NOT an {@link Enum} type then get all declared fields except synthetic fields (ie instrumented
 * additional fields). Where the input IS an {@link Enum} type then also skip the fields that are all the
 * {@link Enum} instances as this would lead to an infinite loop if the user of this class is traversing
 * an object graph.
 */
private List<Field> getFilteredDeclaredFields(Class<?> inputType) {
    return Arrays.asList(inputType.getDeclaredFields()).stream()
                 .filter(field -> !isAnEnum(inputType) ||
                         (isAnEnum(inputType) && !isSameType(field, inputType)))
                 .filter(field -> !field.isSynthetic())
                 .collect(Collectors.toList());

}

private boolean isAnEnum(Class<?> type) {
    return Enum.class.isAssignableFrom(type);
}

private boolean isSameType(Field input, Class<?> ownerType) {
    return input.getType().equals(ownerType);
}

Testklasse in Spock (und Groovy fügt synthetische Felder hinzu):

class ReflectionUtilsSpec extends Specification {

    def "declared fields only"() {

        given: "an instance of a class that does not inherit any fields"
        def instance = new Superclass()

        when: "all fields are requested"
        def result = new ReflectionUtils().getAllFields(instance)

        then: "the fields declared by that instance's class are returned"
        result.size() == 1
        result.findAll { it.name in ['superThing'] }.size() == 1
    }


    def "inherited fields"() {

        given: "an instance of a class that inherits fields"
        def instance = new Subclass()

        when: "all fields are requested"
        def result = new ReflectionUtils().getAllFields(instance)

        then: "the fields declared by that instance's class and its superclasses are returned"
        result.size() == 2
        result.findAll { it.name in ['subThing', 'superThing'] }.size() == 2

    }

    def "no fields"() {
        given: "an instance of a class with no declared or inherited fields"
        def instance = new SuperDooperclass()

        when: "all fields are requested"
        def result = new ReflectionUtils().getAllFields(instance)

        then: "the fields declared by that instance's class and its superclasses are returned"
        result.size() == 0
    }

    def "enum"() {

        given: "an instance of an enum"
        def instance = Item.BIT

        when: "all fields are requested"
        def result = new ReflectionUtils().getAllFields(instance)

        then: "the fields declared by that instance's class and its superclasses are returned"
        result.size() == 3
        result.findAll { it.name == 'smallerItem' }.size() == 1
    }

    private class SuperDooperclass {
    }

    private class Superclass extends SuperDooperclass {
        private String superThing
    }


    private class Subclass extends Superclass {
        private String subThing
    }

    private enum Item {

        BIT("quark"), BOB("muon")

        Item(String smallerItem) {
            this.smallerItem = smallerItem
        }

        private String smallerItem

    }
}
Chris
quelle