Доступ до приватних успадкованих полів через відображення на 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

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

Вихід:

Знайдено поле приватні перехідний булево javax.management.relation.RoleUnresolvedList.typeSafe в класі типу javax.management.relation.RoleUnresolvedList
Знайдено поле особистих минуще булевим javax.management.relation.RoleUnresolvedList.tainted в класі типу javax.management.relation.RoleUnresolvedList
Знайдено поле приватний перехідний java.lang.Object [] java.util.ArrayList.elementData в класі типу java.util.ArrayList
Знайдено поле private int java.util.ArrayList.size в класі типу java.util.ArrayList
Знайдений захищений полем перехідний int java. util.Ab абстрактList.modCount у класі типу java.util.Ab абстрактList


3
це не "шаблон відвідувачів", але це все ще дуже приємний інструмент, якщо у коді є вірус Spring. дякую за те, що поділилися ним :)
thinlizzy

2
@ jose.diego Я впевнений, що ти можеш з цим посперечатися. Він відвідує ієрархію класів, а не дерево об'єктів, але принцип залишається тим самим
Шон Патрік Флойд

Не впевнений, чи отримає цей коментар відповідь, але ви лише одночасно відвідуєте певне поле з цим рішенням. Якщо мені потрібно одночасно переглянути інші поля - наприклад, встановити це поле на "abc", якщо інше поле NULL - я не маю доступного об'єкта в цілому для мене.
ген b.

Це дуже погано, що JavaDoc для цього класу вказує на те, що "він призначений лише для внутрішнього використання", тому це можливий ризик для тих, хто бажає ним користуватися.
космонавт спіф

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 :)
Лукас Кроуфорд

Дякуємо за чудову відповідь ~
Дощ

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
У вектора величезні витрати, тому що все синхронізоване. І там, де вам потрібна синхронізація, є кращі класи в java.util.concurrent. Вектор, 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.