Отримання спадкових імен / значень атрибутів за допомогою відображення Java


128

У мене об’єкт Java 'ChildObj', який розширено від 'ParentObj'. Тепер, якщо можливо отримати всі імена атрибутів та значення ChildObj, включаючи також успадковані атрибути, використовуючи механізм відображення Java?

Class.getFields надає мені масив публічних атрибутів, а Class.getDeclaredFields надає мені масив усіх полів, але жодне з них не включає список успадкованих полів.

Чи є спосіб отримати також успадковані атрибути?

Відповіді:


173

ні, потрібно писати це самостійно. Це простий рекурсивний метод, який називається 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));
}

2
так. думав про це. але хотів перевірити, чи є інший спосіб зробити це. Дякую. :)
Вера,

7
Передати мінливий аргумент і повернути його, ймовірно, не є чудовим дизайном. field.addAll (type.getDeclaredFields ()); було б більш звичайним, ніж посилений для циклу з додаванням.
Том Хоутін - тайклін

Я відчував би необхідність хоча б компілювати його (на stackoverflow!) І, ймовірно, додати трохи Arrays.asList.
Том Хотін - тайклін

Здається, що ваш код збирає всі поля, також приватні та статичні поля, які не успадковуються.
Пітер

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

9
Це моє вподобане рішення, проте я б назвав це "getAllFields", оскільки він також повертає поля даного класу.
Піно

3
Хоча мені дуже подобається рекурсивність (це весело!), Я віддаю перевагу читабельності цього методу та інтуїтивнішим параметрам (не потрібно пропускати нову колекцію), не більше, ніж (неявно в пункті) та немає ітерації над полями себе.
Ремі Морін

це показує, що рекурсивна непотрібна і .. Мені подобаються короткі коди! Дякую! :)
Водолій Сила

Упродовж багатьох років я завжди думаю, що початкове значення для for - це ціле число, з питанням @ Veera, я думаю, що тільки рекурсивний може вирішити це, @ Esko Luontola ваша команда є приголомшливою.
Туя Акіра

@Esko: Дуже дякую Врятував день! Це стисло і працює бездоганно!
gaurav

37

Якщо замість цього ви хотіли покластися на бібліотеку для цього, Apache Commons Lang версії 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));
    }
}

6
Бум! Я люблю не винаходити колесо. Ура за це.
Джошуа Пінтер

6

Вам потрібно зателефонувати:

Class.getSuperclass().getDeclaredFields()

Повторюючи ієрархію спадкування за необхідності.


5

Використовуйте бібліотеку роздумів:

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

4

Рекурсивні рішення добре, єдине невелике питання полягає в тому, що вони повертають набір оголошених і успадкованих членів. Зауважте, що метод getDeclaredFields () повертає також приватні методи. Отже, враховуючи всю ієрархію суперкласу, ви включатимете всі приватні поля, задекларовані в надкласах, і ті, які не успадковуються.

Простий фільтр з Modifier.isPublic || Modifier.isЗахищений предикат буде робити:

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

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

Робоча версія рішення "DidYouMeanThatTomHa ..." вище


2

За допомогою бібліотеки Spring util ви можете перевірити, чи існує один специфічний атрибут у класі:

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

log.info(field2.getName());

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

або

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

 log.info(field2.getName());

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

@cheers



1

Коротше і з меншою кількістю об'єктів? ^^

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

HI @Alexis LEGROS: ArrayUtils не може знайти символ.
Туя Акіра

1
Цей клас походить від Apache Commons Lang.
Алексіс ЛЕГРОС

Apache вже має функцію FieldUtils.getAllFields для обробки цього запиту.
Туя Акіра

1

getFields (): Отримує всі відкриті поля для всієї ієрархії класів та
getDeclaredFields (): Отримує всі поля незалежно від їх модифікаторів, але лише для поточного класу. Отже, ви повинні отримати для всіх ієрархії.
Нещодавно я побачив цей код від 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;
}

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

0

Це переформулювання прийнятої відповіді від @ user1079877. Можливо, версія, яка не змінює параметр функції, а також використовує деякі сучасні функції Java.

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

Ця реалізація також робить виклик трохи більш стислим:

var fields = getFields(MyType.class);

0

Є кілька химер, на які FieldUtils не звертається - зокрема синтетичні поля (наприклад, введені JaCoCo), а також той факт, що тип enum звичайно має поле для кожного екземпляра, і якщо ви перетинаєте графік об'єкта, отримуйте всі поля, а потім отримуючи поля кожного з них тощо, тоді ви потрапите в нескінченний цикл, коли потрапите на перерахунок. Розширене рішення (і якщо чесно, я впевнений, що це має жити десь у бібліотеці!):

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

Тестовий клас у Споку (і Groovy додає синтетичні поля):

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

    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.