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ąć?

benzen
źródło
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
aioobe
źródło
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

Sean Patrick Floyd
źródło
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;
}
jqno
źródło
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)

Exterminator13
źródło
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;
}
benzen
źródło
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;
}
mguymon
źródło
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);
}
Kenny Cason
źródło