Доступ к частным унаследованным полям через отражение в Java


109

Я нашел способ получить унаследованные члены через class.getDeclaredFields(); частные члены и получить доступ к ним через class.getFields() Но я ищу частные унаследованные поля. Как я могу этого добиться?


28
"унаследованные частные поля" не существуют. Если поле является частным, оно не наследуется и остается только в области родительского класса. Чтобы получить доступ к родительским частным полям, вы должны сначала получить доступ к родительскому классу (см. Ответ aioobe)
— Бенуа Куртин

6
при этом защищенные поля наследуются, но вы должны сделать то же самое, чтобы получить их путем отражения.
— Божо

Ответы:


128

Это должно продемонстрировать, как ее решить:

import java.lang.reflect.Field;

class Super {
    private int i = 5;
}

public class B extends Super {
    public static void main(String[] args) throws Exception {
        B b = new B();
        Field f = b.getClass().getSuperclass().getDeclaredField("i");
        f.setAccessible(true);
        System.out.println(f.get(b));
    }
}

(Или Class.getDeclaredFieldsдля массива всех полей.)

Вывод:

5

Получены ли все поля суперклассов или только прямой суперкласс?
— Дождь

Поля прямых суперклассов. Вы можете повторять, getSuperclass()пока не достигнете, nullесли хотите подняться выше.
— aioobe

Почему бы вам не использовать getDeclaredFields()[0]или, getDeclaredField("i")скорее, повторить [0]доступ к массиву в следующих двух операторах?
— Хольгер

Это связано с тем, как сформулирован этот конкретный вопрос. По сути, это была просто демонстрация того, как пользоваться getDeclaredFields. Ответ обновлен.
— aioobe 03

44

Лучшим подходом здесь является использование шаблона посетителя, чтобы найти все поля в классе и все суперклассы и выполнить для них действие обратного вызова.


Реализация

Spring имеет хороший класс Utility, ReflectionUtilsкоторый делает именно это: он определяет метод для цикла по всем полям всех суперклассов с помощью обратного вызова:ReflectionUtils.doWithFields()

Документация:

Вызвать данный обратный вызов для всех полей в целевом классе, поднявшись по иерархии классов, чтобы получить все объявленные поля.

Параметры:
- clazz - целевой класс для анализа
- fc - обратный вызов, вызываемый для каждого поля
- ff - фильтр, определяющий поля, к которым применяется обратный вызов

Образец кода:

ReflectionUtils.doWithFields(RoleUnresolvedList.class,
    new FieldCallback(){

        @Override
        public void doWith(final Field field) throws IllegalArgumentException,
            IllegalAccessException{

            System.out.println("Found field " + field + " in type "
                + field.getDeclaringClass());

        }
    },
    new FieldFilter(){

        @Override
        public boolean matches(final Field field){
            final int modifiers = field.getModifiers();
            // no static fields please
            return !Modifier.isStatic(modifiers);
        }
    });

Вывод:

Найдено поле private transient boolean javax.management.relation.RoleUnresolvedList.typeSafe в классе типа javax.management.relation.RoleUnresolvedList
Найдено поле private transient boolean javax.management.relation.RoleUnresolvedList.tainted в
поле класса типа javax.management.relation.RoleUnresolvedListole private transient java.lang.Object [] java.util.ArrayList.elementData в классе типа java.util.ArrayList
Найдено поле private int java.util.ArrayList.size в классе типа java.util.ArrayList
Найдено поле, защищенное временным интервалом int java. util.AbstractList.modCount в классе типов java.util.AbstractList


3
это не «шаблон посетителя», но это все равно очень хороший инструмент, если в вашем коде есть вирус Spring. спасибо, что поделился этим :)
— thinlizzy

2
@ jose.diego Я почти уверен, что вы можете поспорить об этом. Он обращается к иерархии классов, а не к дереву объектов, но принцип остается тем же
— Шон Патрик Флойд

Не уверен, получит ли этот комментарий ответ, но с этим решением вы одновременно посещаете только определенное поле. Если мне нужно одновременно просмотреть другие поля - например, установить для этого поля значение «abc», если другое поле - ПУСТО (NULL) - у меня нет доступного мне объекта в целом.
— ген b.

Очень жаль, что JavaDoc для этого класса указывает, что «он предназначен только для внутреннего использования», так что это возможный риск для любого, кто хочет его использовать.
— космонавт spiff

1
@spacemanspiff вы технически правы, но этот класс существует уже около 15 лет (включая 4 основные версии выпуска) и широко используется многими клиентами Spring. Сомневаюсь, что они его сейчас отзовут.
— Шон Патрик Флойд

34

Это сделает это:

private List<Field> getInheritedPrivateFields(Class<?> type) {
    List<Field> result = new ArrayList<Field>();

    Class<?> i = type;
    while (i != null && i != Object.class) {
        Collections.addAll(result, i.getDeclaredFields());
        i = i.getSuperclass();
    }

    return result;
}

Если вы используете инструмент покрытия кода, такой как EclEmma , вы должны быть осторожны : они добавляют скрытое поле в каждый из ваших классов. В случае EclEmma эти поля помечены как синтетические , и вы можете отфильтровать их следующим образом:

private List<Field> getInheritedPrivateFields(Class<?> type) {
    List<Field> result = new ArrayList<Field>();

    Class<?> i = type;
    while (i != null && i != Object.class) {
        for (Field field : i.getDeclaredFields()) {
            if (!field.isSynthetic()) {
                result.add(field);
            }
        }
        i = i.getSuperclass();
    }

    return result;
}

Спасибо за ваше замечание о синтетических полях, EMMA делает то же самое.
— Анатолий

он получает объявленные и унаследованные поля класса аргументов, поэтому его следует назвать getDeclaredAndInheritedPrivateFields. отлично, хотя спасибо!
— Питер Хокинс

1
хороший улов на isSynthetic :)
— Лукас Кроуфорд

Спасибо за отличный ответ ~
— Raining

19
public static Field getField(Class<?> clazz, String fieldName) {
    Class<?> tmpClass = clazz;
    do {
        try {
            Field f = tmpClass.getDeclaredField(fieldName);
            return f;
        } catch (NoSuchFieldException e) {
            tmpClass = tmpClass.getSuperclass();
        }
    } while (tmpClass != null);

    throw new RuntimeException("Field '" + fieldName
            + "' not found on class " + clazz);
}

(на основе этого ответа)


15

На самом деле я использую иерархию сложного типа, поэтому ваше решение не является полным. Мне нужно сделать рекурсивный вызов, чтобы получить все унаследованные частные поля. Вот мое решение

 /**
 * Return the set of fields declared at all level of class hierachy
 */
public Vector<Field> getAllFields(Class clazz) {
    return getAllFieldsRec(clazz, new Vector<Field>());
}

private Vector<Field> getAllFieldsRec(Class clazz, Vector<Field> vector) {
    Class superClazz = clazz.getSuperclass();
    if(superClazz != null){
        getAllFieldsRec(superClazz, vector);
    }
    vector.addAll(toVector(clazz.getDeclaredFields()));
    return vector;
}

Однако его решение привело вас на верный путь, не так ли?
— aperkins

1
Вектор - это старый плохой код. Пожалуйста, используйте текущую структуру данных из структуры коллекций (в большинстве случаев подходит ArrayList)
— Шон Патрик Флойд,

@aperkins ответ aioobe похож на мой, но я нашел его, а потом увидел ответ. @seanizer Vector не так уж и стар, и он входит в состав API коллекции
— benzen

«Начиная с платформы Java 2 v1.2, этот класс был модифицирован для реализации List, так что он стал частью структуры сбора данных Java». дооснащен в 1.2? если это не старое, тогда что? Источник: download.oracle.com/javase/1.4.2/docs/api/java/util/Vector.html
— Шон Патрик Флойд,

7
Vector имеет огромные накладные расходы, потому что все синхронизировано. А там, где вам нужна синхронизация, есть классы получше в java.util.concurrent. Vector, Hashtable и StringBuffer в большинстве случаев следует заменить на ArrayList, HashMap и StringBuilder
— Шон Патрик Флойд,

8

Мне нужно было добавить поддержку унаследованных полей для чертежей в Model Citizen . Я получил этот метод, который является немного более кратким для получения полей класса + унаследованных полей.

private List<Field> getAllFields(Class clazz) {
    List<Field> fields = new ArrayList<Field>();

    fields.addAll(Arrays.asList(clazz.getDeclaredFields()));

    Class superClazz = clazz.getSuperclass();
    if(superClazz != null){
        fields.addAll(getAllFields(superClazz));
    }

    return fields;
}

7
private static Field getField(Class<?> clazz, String fieldName) {
    Class<?> tmpClass = clazz;
    do {
        for ( Field field : tmpClass.getDeclaredFields() ) {
            String candidateName = field.getName();
            if ( ! candidateName.equals(fieldName) ) {
                continue;
            }
            field.setAccessible(true);
            return field;
        }
        tmpClass = tmpClass.getSuperclass();
    } while ( clazz != null );
    throw new RuntimeException("Field '" + fieldName +
        "' not found on class " + clazz);
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.