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