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.