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

109

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

Benzen
источник
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
aioobe
источник
Получены ли все поля суперклассов или только прямой суперкласс?
Дождь
Поля прямых суперклассов. Вы можете повторять, 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;
}
jqno
источник
Спасибо за ваше замечание о синтетических полях, 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);
}

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

Истребитель13
источник
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;
}
Benzen
источник
Однако его решение привело вас на верный путь, не так ли?
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;
}
Mguymon
источник
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);
}
Кенни Кейсон
источник