Получение унаследованных имен / значений атрибутов с помощью Java Reflection

У меня есть объект Java «ChildObj», который является расширением «ParentObj». Теперь, можно ли получить все имена атрибутов и значения ChildObj, включая унаследованные атрибуты, используя механизм отражения Java?

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

Есть ли способ получить также унаследованные атрибуты?


person Veera    schedule 25.06.2009    source источник


Ответы (13)


нет, это нужно написать самому. Это простой рекурсивный метод, вызываемый 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));
}
person dfa    schedule 25.06.2009
comment
Передача изменяемого аргумента и его возврат, вероятно, не лучший дизайн. fields.addAll (тип.getDeclaredFields ()); будет более обычным, чем усовершенствованный цикл for с добавлением. - person Tom Hawtin - tackline; 25.06.2009
comment
Я бы почувствовал необходимость хотя бы скомпилировать его (в stackoverflow!) И, возможно, добавить немного Arrays.asList. - person Tom Hawtin - tackline; 25.06.2009
comment
Кажется, что ваш код собирает все поля, а также частные и статические поля, которые не наследуются. - person Peter Verhas; 12.02.2019

Если вместо этого вы хотите полагаться на библиотеку для достижения этой цели, 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));
    }
}
person Chris    schedule 28.05.2014

Вам необходимо позвонить:

Class.getSuperclass().getDeclaredFields()

При необходимости повторное использование иерархии наследования.

person Nick Holt    schedule 25.06.2009

Использовать библиотеку отражений:

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

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

Простой фильтр с Modifier.isPublic || Предикат Modifier.isProtected будет делать:

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);
    }
}
person Marek Dec    schedule 22.12.2011

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;
}
person Aman    schedule 01.02.2019
comment
Это лучше, чем лучший ответ, потому что цикл while всегда быстрее, чем рекурсивные вызовы. Но я думаю, что LinkedList как структура данных более подходит вместо ArrayList, потому что ArraysList будет изменен (несколько раз), когда размер списка достигнет значения емкости, что повлечет за собой временные затраты - person Alex; 03.03.2021
comment
@Alex Я определенно согласен, я полагаю, что они выбрали ArrayList вместо LinkedList, учитывая большой объем памяти, занимаемый LinkedList. - person Aman; 06.03.2021

С помощью библиотеки spring util вы можете использовать, чтобы проверить, существует ли один конкретный атрибут в классе:

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

log.info(field2.getName());

Документ API:
https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/util/ReflectionUtils.html

or

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

 log.info(field2.getName());

Документ API:
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/util/ReflectionUtils.html

@ваше здоровье

person Marcelo Rebouças    schedule 05.05.2020

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 ..." выше

person Theo Platt    schedule 17.02.2011

Можешь попробовать:

   Class parentClass = getClass().getSuperclass();
   if (parentClass != null) {
      parentClass.getDeclaredFields();
   }
person Manuel Selva    schedule 25.06.2009

Короче и с меньшим количеством экземпляров объекта? ^^

private static Field[] getAllFields(Class<?> type) {
    if (type.getSuperclass() != null) {
        return (Field[]) ArrayUtils.addAll(getAllFields(type.getSuperclass()), type.getDeclaredFields());
    }
    return type.getDeclaredFields();
}
person Alexis LEGROS    schedule 07.05.2015
comment
Привет @Alexis LEGROS: ArrayUtils не может найти символ. - person Touya Akira; 09.09.2017
comment
Этот класс взят из Apache Commons Lang. - person Alexis LEGROS; 13.09.2017
comment
В Apache уже есть функция FieldUtils.getAllFields для обработки этого запроса вопроса. - person Touya Akira; 13.09.2017

Это переработка принятого ответа @ 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);
person scrutari    schedule 19.01.2019

Есть несколько причуд, которые не решаются FieldUtils - в частности, синтетические поля (например, введенные JaCoCo), а также тот факт, что тип перечисления, конечно, имеет поле для каждого экземпляра, и если вы просматриваете граф объекта, получение все поля, а затем получение полей каждого из них и т. д., тогда вы попадете в бесконечный цикл, когда нажмете перечисление. Расширенное решение (и, честно говоря, я уверен, что оно должно находиться где-то в библиотеке!) Будет:

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

Тестовый класс в Spock (а 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

    }
}
person Chris    schedule 21.11.2019

person    schedule
comment
Это мое предпочтительное решение, однако я бы назвал его getAllFields, потому что оно также возвращает поля данного класса. - person Pino; 15.05.2012
comment
Хотя мне очень нравится рекурсивность (это весело!), Я предпочитаю удобочитаемость этого метода и более интуитивно понятные параметры (не требуется, чтобы новая коллекция передавалась), не более if (неявно в предложении for) и отсутствие итерации по полям самих себя. - person Remi Morin; 10.09.2013
comment
он показывает, что в рекурсии нет необходимости и .. Мне нравятся короткие коды! Спасибо! :) - person Aquarius Power; 04.03.2015
comment
На протяжении многих лет я всегда думаю, что начальное значение для - это просто целое число, с вопросом @ Veera я думаю, что только рекурсивный может решить его, @ Esko Luontola ваша команда потрясающая. - person Touya Akira; 09.09.2017