Accesso a campi ereditati privati ​​tramite reflection in Java


109

Ho trovato un modo per ottenere membri ereditati tramite class.getDeclaredFields(); e accedere a membri privati ​​tramite class.getFields() Ma sto cercando campi ereditati privati. Come posso raggiungere questo obiettivo?


28
"campi ereditati privati" non esistono. Se un campo è privato, non viene ereditato e rimane solo nell'ambito della classe genitore. Per accedere ai campi privati ​​del genitore, devi prima accedere alla classe genitore (cfr. La risposta di aioobe)
Benoit Courtine

6
Detto questo, i campi protetti vengono ereditati, ma devi fare lo stesso per ottenerli per riflessione.
Bozho

Risposte:


128

Questo dovrebbe dimostrare come risolverlo:

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

(O Class.getDeclaredFieldsper un array di tutti i campi.)

Produzione:

5

Ottiene tutti i campi delle superclassi o solo la superclasse diretta?
Piove

Campi diretti delle super classi. Puoi ricorrere getSuperclass()fino a raggiungere nullse vuoi andare più in alto.
aioobe

Perché non usi getDeclaredFields()[0]o getDeclaredField("i")piuttosto ripeti l' [0]accesso all'array nelle prossime due istruzioni?
Holger

È dovuto al modo in cui è formulata questa particolare domanda. Fondamentalmente era solo una dimostrazione di come usare getDeclaredFields. La risposta è stata aggiornata.
aioobe

44

L'approccio migliore qui è usare il Visitor Pattern per trovare tutti i campi nella classe e tutte le super classi ed eseguire un'azione di callback su di essi.


Implementazione

Spring ha una bella classe Utility ReflectionUtilsche fa proprio questo: definisce un metodo per eseguire il ciclo su tutti i campi di tutte le super classi con un callback:ReflectionUtils.doWithFields()

Documentazione:

Richiama la richiamata data su tutti i campi nella classe di destinazione, risalendo la gerarchia delle classi per ottenere tutti i campi dichiarati.

Parametri:
- clazz - la classe di destinazione da analizzare
- fc - il callback da invocare per ogni campo
- ff - il filtro che determina i campi a cui applicare il callback

Codice di esempio:

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

Produzione:

Trovato campo booleano transitorio privato javax.management.relation.RoleUnresolvedList.typeSafe nella classe di tipo javax.management.relation.RoleUnresolvedList
Trovato campo booleano transitorio privato javax.management.relation.RoleUnresolvedList.tainted nella classe di tipo javax.management
campo FoundRole.Role. private transient java.lang.Object [] java.util.ArrayList.elementData nel tipo class java.util.ArrayList
Trovato campo private int java.util.ArrayList.size nel tipo class java.util.ArrayList
Trovato campo protetto transient int java. util.AbstractList.modCount nella classe di tipo java.util.AbstractList


3
questo non è un "pattern visitatore", ma è comunque uno strumento molto carino se hai il virus Spring nel tuo codice. grazie per averlo condiviso :)
thinlizzy

2
@ jose.diego Sono abbastanza sicuro che potresti discuterne. Visita una gerarchia di classi piuttosto che un albero di oggetti, ma il principio rimane lo stesso
Sean Patrick Floyd

Non sono sicuro che questo commento ottenga una risposta, ma con questa soluzione stai visitando solo un campo particolare alla volta. Se ho bisogno di guardare altri campi allo stesso tempo - ad esempio, imposta questo campo su "abc" se un altro campo è NULL - non ho l'oggetto nel suo complesso a mia disposizione.
gene b.

È un peccato che JavaDoc per questa classe indichi che "è inteso solo per uso interno", quindi questo è un possibile rischio per chiunque desideri usarlo.
spaceman spiff

1
@spacemanspiff sei tecnicamente corretto, ma questa classe esiste da circa 15 anni (incluse 4 versioni principali) ed è stata ampiamente utilizzata da molti clienti Spring. Dubito che lo ritireranno ora.
Sean Patrick Floyd

34

Questo lo farà:

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

Se usi uno strumento di copertura del codice come EclEmma , devi stare attento: aggiungono un campo nascosto a ciascuna delle tue classi. Nel caso di EclEmma, ​​questi campi sono contrassegnati come sintetici e puoi filtrarli in questo modo:

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

Grazie per il tuo commento sui campi sintetici, EMMA fa lo stesso.
Anatoliy

questo ottiene i campi dichiarati ed ereditati della classe argomento, quindi dovrebbe essere chiamato getDeclaredAndInheritedPrivateFields. perfetto però grazie!
Peter Hawkins

1
bella presa su isSynthetic :)
Lucas Crawford

Grazie per la superba risposta ~
Piove

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

(basato su questa risposta)


15

In effetti uso una gerarchia di tipi complessa, quindi la soluzione non è completa. Devo effettuare una chiamata ricorsiva per ottenere tutti i campi ereditati privati. Ecco la mia soluzione

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

Tuttavia, la sua soluzione ti ha portato sulla strada giusta, no?
aperkins

1
Il vettore è un vecchio codice cattivo. Si prega di utilizzare una struttura dati corrente dal framework delle raccolte (ArrayList è adeguato nella maggior parte dei casi)
Sean Patrick Floyd

@aperkins la risposta di aioobe sembra la mia, ma l'ho trovata e poi ho visto la risposta. @seanizer Vector non è così vecchio ed è un membro della raccolta API
benzen

"A partire dalla piattaforma Java 2 v1.2, questa classe è stata aggiornata per implementare List, in modo che diventi parte del framework di raccolta di Java." retrofit in 1.2? se non è vecchio allora cos'è? Fonte: download.oracle.com/javase/1.4.2/docs/api/java/util/Vector.html
Sean Patrick Floyd

7
Vector ha un sovraccarico enorme perché tutto è sincronizzato. E dove è necessaria la sincronizzazione, ci sono classi migliori in java.util.concurrent. Vector, Hashtable e StringBuffer dovrebbero nella maggior parte dei casi essere sostituiti da ArrayList, HashMap e StringBuilder
Sean Patrick Floyd

8

Avevo bisogno di aggiungere il supporto per i campi ereditati per i progetti in Model Citizen . Ho derivato questo metodo che è un po 'più conciso per recuperare i campi di una classe + i campi ereditati.

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);
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.