web-dev-qa-db-fra.com

Accès aux champs hérités privés via réflexion dans Java

J'ai trouvé un moyen d'obtenir des membres hérités via class.getDeclaredFields(); et d'accéder aux membres privés via class.getFields(), mais je recherche des champs hérités privés. Comment puis-je atteindre cet objectif?

104
benzen

En fait, j'utilise une hiérarchie de types complexes, de sorte que votre solution n'est pas complète. Je dois faire un appel récursif pour obtenir tous les champs hérités privés. Voici ma solution

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

Cela devrait montrer comment le résoudre:

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[] fs = b.getClass().getSuperclass().getDeclaredFields();
        fs[0].setAccessible(true);
        System.out.println(fs[0].get(b));
    }
}

Sortie:

5
123
aioobe

La meilleure approche consiste ici à utiliser le Visitor Pattern trouve tous les champs de la classe et toutes les super classes et exécute une action de rappel sur eux.


La mise en oeuvre

Spring a une classe Nice Utility ReflectionUtils qui ne fait que cela: elle définit une méthode pour boucler tous les champs de toutes les super classes avec un callback: ReflectionUtils.doWithFields()

Documentation:

Appelez le rappel donné sur tous les champs de la classe cible, en remontant la hiérarchie de la classe pour obtenir tous les champs déclarés.

Paramètres:
- clazz - la classe cible à analyser
- fc - le rappel à invoquer pour chaque champ
- ff - le filtre qui détermine les champs auxquels appliquer le rappel

Exemple de code:

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

Sortie:

Champ trouvé privé transitoire booléen javax.management.relation.RoleUnresolvedList.typeSafe dans la classe type javax.management.relation.RoleUnresolvedList
Champ trouvé transitoire privé booléen javax.management.relation.RoleUnresolvedList.tainted dans la classe type javax.management.relation.RoleUnresolvedList
Champ trouvé transitoire privé Java.lang.Object [] Java.util.ArrayList.elementData dans la classe de type Java.util.ArrayList
Champ privé int trouvé dans Java.util.ArrayList.size dans la classe de type Java.util.ArrayList
Champ trouvé transitoire protégé int Java.util.AbstractList.modCount dans la classe type Java.util.AbstractList

44
Sean Patrick Floyd

Ça va le faire:

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

Si vous utilisez un outil de couverture de code tel que EclEmma , vous devez faire attention: ils ajoutent un champ caché à chacune de vos classes. Dans le cas de EclEmma, ​​ces champs sont marqués synthétiques , et vous pouvez les filtrer comme suit:

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

(basé sur this réponse)

19
Exterminator13

J'avais besoin d'ajouter un support pour les champs hérités pour les plans en Model Citizen . J'ai dérivé cette méthode qui est un peu plus concise pour récupérer les champs + les champs hérités d'une classe.

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;
}
8
mguymon
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);
}
7
Kenny Cason