Dostęp do dziedziczonych prywatnych pól poprzez odbicie w Javie


109

Znalazłem sposób na zdobycie dziedziczonych członków przez class.getDeclaredFields(); i dostęp do prywatnych członków przez class.getFields() Ale ja szukam prywatnych dziedziczonych pól. Jak mogę to osiągnąć?


28
„Dziedziczone pola prywatne” nie istnieje. Jeśli pole jest prywatne, nie jest dziedziczone i pozostaje tylko w zakresie klasy nadrzędnej. Dostęp do dominujących pól prywatnych, trzeba dostępowego pierwszej klasy nadrzędnej (por aioobe reagowania)
— Benoit Courtine

6
To powiedziawszy, pola chronione są dziedziczone, ale musisz zrobić to samo, aby uzyskać je przez odbicie.
— Bozho,

Odpowiedzi:


128

Powinno to pokazać, jak go rozwiązać:

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

(Lub Class.getDeclaredFieldsdla tablicy wszystkich pól.)

Wynik:

5

Czy to obejmuje pola wszystkich nadklas, czy tylko bezpośrednią superklasę?
— Deszcz

Bezpośrednie pola superklas. Możesz powtarzać, getSuperclass()aż osiągniesz, nulljeśli chcesz iść wyżej.
— aioobe

Dlaczego nie używasz getDeclaredFields()[0]lub getDeclaredField("i")raczej powtarzasz [0]dostęp do tablicy w następnych dwóch instrukcjach?
— Holger

Wynika to ze sposobu sformułowania tego konkretnego pytania. To była w zasadzie tylko demonstracja, jak używać getDeclaredFields. Odpowiedź została zaktualizowana.
— aioobe

44

Najlepszym podejściem w tym przypadku jest użycie wzorca gościa, aby znaleźć wszystkie pola w klasie i wszystkie superklasy i wykonać na nich akcję zwrotną.


Realizacja

Spring ma ładną klasę Utility ReflectionUtils, która właśnie to robi: definiuje metodę pętli po wszystkich polach wszystkich superklas z wywołaniem zwrotnym:ReflectionUtils.doWithFields()

Dokumentacja:

Wywołaj podane wywołanie zwrotne we wszystkich polach w klasie docelowej, przechodząc w górę hierarchii klas w celu pobrania wszystkich zadeklarowanych pól.

Parametry:
- clazz - klasa docelowa do analizy
- fc - wywołanie zwrotne do wywołania dla każdego pola
- ff - filtr określający pola do zastosowania wywołania zwrotnego

Przykładowy kod:

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

Wynik:

Znaleziono pola prywatne przemijający logiczna javax.management.relation.RoleUnresolvedList.typeSafe w klasie typu javax.management.relation.RoleUnresolvedList
znaleźć pole prywatny przejściowy logiczna javax.management.relation.RoleUnresolvedList.tainted w klasie typu javax.management.relation.RoleUnresolvedList
Znalezione dziedzinie private przejściowy java.lang.Object [] java.util.ArrayList.elementData w klasie typu java.util.ArrayList
Znalezione pole prywatne int java.util.ArrayList.size w klasie typu java.util.ArrayList
Znalezione pole chronione przejściowo int java. util.AbstractList.modCount w klasie typu java.util.AbstractList


3
to nie jest „wzorzec odwiedzających”, ale nadal jest to bardzo fajne narzędzie, jeśli masz wirusa Spring w swoim kodzie. dzięki za udostępnienie :)
— thinlizzy

2
@ jose.diego Jestem pewien, że mógłbyś się o to spierać. Odwiedza hierarchię klas, a nie drzewo obiektów, ale zasada pozostaje ta sama
— Sean Patrick Floyd

Nie jestem pewien, czy ten komentarz otrzyma odpowiedź, ale dzięki temu rozwiązaniu odwiedzasz tylko określone pole naraz. Jeśli muszę spojrzeć na inne pola w tym samym czasie - np. Ustawić to pole na „abc”, jeśli inne pole ma wartość NULL - nie mam do dyspozycji całego obiektu.
— gen b.

Szkoda, że ​​JavaDoc dla tej klasy wskazuje, że „jest przeznaczony tylko do użytku wewnętrznego”, więc jest to potencjalne zagrożenie dla każdego, kto chce z niej korzystać.
— spaceman spiff

1
@spacemanspiff masz techniczną poprawność, ale ta klasa istnieje od około 15 lat (w tym 4 główne wersje) i jest szeroko używana przez wielu klientów Spring. Wątpię, żeby teraz to cofnęli.
— Sean Patrick Floyd

34

To wystarczy:

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

Jeśli używasz narzędzia do pokrycia kodu, takiego jak EclEmma , musisz uważać: dodają one ukryte pole do każdej z twoich klas. W przypadku EclEmma te pola są oznaczone jako syntetyczne i możesz je odfiltrować w następujący sposób:

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

Dziękuję za uwagę na temat pól syntetycznych, EMMA robi to samo.
— Anatoliy

Pobiera zadeklarowane i dziedziczone pola klasy argumentu, więc powinno nazywać się getDeclaredAndInheritedPrivateFields. idealne chociaż dzięki!
— Peter Hawkins,

1
niezły chwyt na isSynthetic :)
— Lucas Crawford

Dzięki za doskonałą odpowiedź ~
— Pada

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

(na podstawie tej odpowiedzi)


15

W rzeczywistości używam złożonej hierarchii typów, więc rozwiązanie nie jest kompletne. Muszę wykonać wywołanie rekurencyjne, aby pobrać wszystkie prywatne pola dziedziczone. Oto moje rozwiązanie

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

Jednak jego rozwiązanie zaprowadziło cię na właściwą ścieżkę, prawda?
— aperkins

1
Wektor to zły stary kod. Proszę korzystać z obecnej struktury danych z ramami kolekcji (ArrayList jest wystarczające w większości przypadków)
— Sean Patrick Floyd

@aperkins odpowiedź aioobe wygląda jak moja, ale znalazłem ją i wtedy zobaczyłem odpowiedź. @seanizer Vector nie jest taki stary i jest członkiem interfejsu API kolekcji
— benzen

„Począwszy od platformy Java 2 v1.2, ta klasa została zmodernizowana w celu zaimplementowania List, dzięki czemu stała się częścią struktury kolekcji Java”. zmodernizowany w 1.2? jeśli to nie jest stare, to co jest? Źródło: download.oracle.com/javase/1.4.2/docs/api/java/util/Vector.html
— Sean Patrick Floyd,

7
Vector ma ogromny narzut, ponieważ wszystko jest zsynchronizowane. A tam, gdzie potrzebujesz synchronizacji, są lepsze klasy w java.util.concurrent. Vector, Hashtable i StringBuffer w większości przypadków powinny być zastąpione przez ArrayList, HashMap i StringBuilder
— Sean Patrick Floyd

8

Musiałem dodać obsługę dziedziczonych pól dla planów w Model Citizen . Wyprowadziłem tę metodę, która jest nieco bardziej zwięzła do pobierania pól klasy + dziedziczonych pól.

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);
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.