De overgenomen attribuutnamen/waarden ophalen met Java Reflection

Ik heb een Java-object ‘ChildObj’ dat is uitgebreid met ‘ParentObj’. Als het nu mogelijk is om alle attribuutnamen en -waarden van ChildObj op te halen, inclusief de geërfde attributen, met behulp van het Java-reflectiemechanisme?

Class.getFieldsgeeft me de reeks openbare attributen en Class.getDeclaredFieldsgeeft me de array van alle velden, maar geen van hen bevat de lijst met geërfde velden.

Is er ook een manier om de overgeërfde attributen op te halen?


Antwoord 1, autoriteit 100%

nee, je moet het zelf schrijven. Het is een eenvoudige recursieve methode die wordt aangeroepen op Class.getSuperClass():

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

Antwoord 2, autoriteit 48%

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

Antwoord 3, autoriteit 19%

Als u in plaats daarvan op een bibliotheek wilt vertrouwen om dit te bereiken, biedt Apache Commons Langversie 3.2+ 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));
    }
}

Antwoord 4, autoriteit 4%

U moet bellen:

Class.getSuperclass().getDeclaredFields()

Zo nodig herhalen van de overervingshiërarchie.


Antwoord 5, autoriteit 2%

Gebruik Reflections-bibliotheek:

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

Antwoord 6, autoriteit 2%

getFields():
Haalt alle openbare velden omhoog in de hele klassenhiërarchie en
getDeclaredFields():
Krijgt alle velden, ongeacht hun modifiers, maar alleen voor de huidige klasse. Dus je moet voor alle betrokken hiërarchie komen.

Ik zag onlangs deze code van org.apache.commons.lang3.reflect.FieldUtils

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

Antwoord 7, autoriteit 2%

De recursieve oplossingen zijn OK, het enige kleine probleem is dat ze een superset van gedeclareerde en geërfde leden retourneren. Merk op dat de methode getDeclaredFields() ook privémethoden retourneert. Aangezien u dus door de hele hiërarchie van superklassen navigeert, neemt u alle privévelden op die in de superklassen zijn gedeclareerd, en die worden niet overgenomen.

Een eenvoudig filter met een Modifier.isPublic || Modifier.isProtected predikaat zou doen:

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

Antwoord 8, autoriteit 2%

Met spring util-bibliotheek kunt u gebruiken om te controleren of een specifiek kenmerk in de klasse bestaat:

Field field = ReflectionUtils.findRequiredField(YOUR_CLASS.class, "ATTRIBUTE_NAME");
log.info(field2.getName());

Api-document:

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

of

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

Api-document:

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

@proost


Antwoord 9

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

Werkende versie van de “DidYouMeanThatTomHa…”-oplossing hierboven


Antwoord 10

Je kunt het proberen:

  Class parentClass = getClass().getSuperclass();
   if (parentClass != null) {
      parentClass.getDeclaredFields();
   }

Antwoord 11

Korter en met minder geïnstantieerd object? ^^

private static Field[] getAllFields(Class<?> type) {
    if (type.getSuperclass() != null) {
        return (Field[]) ArrayUtils.addAll(getAllFields(type.getSuperclass()), type.getDeclaredFields());
    }
    return type.getDeclaredFields();
}

Antwoord 12

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

Antwoord 13

Dit is een herformulering van het geaccepteerde antwoord door @user1079877. Het kan zijn dat een versie geen parameter van de functie wijzigt en ook enkele moderne Java-functies gebruikt.

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

Deze implementatie maakt het aanroepen ook wat beknopter:

var fields = getFields(MyType.class);

Antwoord 14

Er zijn een paar eigenaardigheden die niet worden aangepakt door FieldUtils – met name synthetische velden (bijvoorbeeld geïnjecteerd door JaCoCo) en ook het feit dat een enum-type natuurlijk een veld heeft voor elke instantie, en als je een object doorkruist grafiek, alle velden krijgen en dan de velden van elk van hen krijgen enz., dan kom je in een oneindige lus wanneer je een opsomming raakt. Een uitgebreide oplossing (en om eerlijk te zijn weet ik zeker dat dit ergens in een bibliotheek moet staan!) zou zijn:

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

Testles in Spock (en Groovy voegt synthetische velden toe):

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

Other episodes