web-dev-qa-db-fra.com

Récupération des noms/valeurs d'attributs hérités à l'aide de Java Reflection

J'ai un objet Java 'ChildObj' qui est étendu à partir de 'ParentObj'. Maintenant, s’il est possible de récupérer tous les noms et valeurs d’attributs de ChildObj, y compris les attributs hérités, à l’aide du mécanisme de réflexion Java?

Class.getFields me donne le tableau d'attributs publics et Class.getDeclaredFields me donne le tableau de tous les champs, mais aucun d'eux ne comprend la liste des champs hérités.

Est-il possible de récupérer également les attributs hérités?

107
Veera

non, vous devez l'écrire vous-même. C'est une méthode récursive simple appelée sur Class.getSuperClass () :

public static List<Field> getAllFields(List<Field> fields, Class<?> type) {
    fields.addAll(Arrays.asList(type.getDeclaredFields()));

    if (type.getSuperclass() != null) {
        getAllFields(fields, type.getSuperclass());
    }

    return fields;
}

@Test
public void getLinkedListFields() {
    System.out.println(getAllFields(new LinkedList<Field>(), LinkedList.class));
}
155
dfa
    public static List<Field> getAllFields(Class<?> type) {
        List<Field> fields = new ArrayList<Field>();
        for (Class<?> c = type; c != null; c = c.getSuperclass()) {
            fields.addAll(Arrays.asList(c.getDeclaredFields()));
        }
        return fields;
    }
77
Esko Luontola

Si, au lieu de cela, vous vouliez vous fier à une bibliothèque, Apache Commons Lang la version 3.2+ fournit FieldUtils.getAllFieldsList :

import Java.lang.reflect.Field;
import Java.util.AbstractCollection;
import Java.util.AbstractList;
import Java.util.AbstractSequentialList;
import Java.util.Arrays;
import Java.util.LinkedList;
import Java.util.List;

import org.Apache.commons.lang3.reflect.FieldUtils;
import org.junit.Assert;
import org.junit.Test;

public class FieldUtilsTest {

    @Test
    public void testGetAllFieldsList() {

        // Get all fields in this class and all of its parents
        final List<Field> allFields = FieldUtils.getAllFieldsList(LinkedList.class);

        // Get the fields form each individual class in the type's hierarchy
        final List<Field> allFieldsClass = Arrays.asList(LinkedList.class.getFields());
        final List<Field> allFieldsParent = Arrays.asList(AbstractSequentialList.class.getFields());
        final List<Field> allFieldsParentsParent = Arrays.asList(AbstractList.class.getFields());
        final List<Field> allFieldsParentsParentsParent = Arrays.asList(AbstractCollection.class.getFields());

        // Test that `getAllFieldsList` did truly get all of the fields of the the class and all its parents 
        Assert.assertTrue(allFields.containsAll(allFieldsClass));
        Assert.assertTrue(allFields.containsAll(allFieldsParent));
        Assert.assertTrue(allFields.containsAll(allFieldsParentsParent));
        Assert.assertTrue(allFields.containsAll(allFieldsParentsParentsParent));
    }
}
32
Chris

Vous devez appeler:

Class.getSuperclass().getDeclaredFields()

Récursant la hiérarchie d'héritage si nécessaire.

6
Nick Holt

Utilisez la bibliothèque de réflexions:

public Set<Field> getAllFields(Class<?> aClass) {
    return org.reflections.ReflectionUtils.getAllFields(aClass);
}
5
Lukasz Ochmanski

Les solutions récursives sont acceptables. Le seul petit problème est qu’elles renvoient un sur-ensemble de membres déclarés et hérités. Notez que la méthode getDeclaredFields () renvoie également les méthodes privées. Donc, étant donné que vous naviguez dans l’ensemble de la hiérarchie de la superclasse, vous incluez tous les champs privés déclarés dans les superclasses, qui ne sont pas hérités.

Un filtre simple avec un modificateur.isPublic || Le prédicat modificateur.isProtected ferait:

import static Java.lang.reflect.Modifier.isPublic;
import static Java.lang.reflect.Modifier.isProtected;

(...)

List<Field> inheritableFields = new ArrayList<Field>();
for (Field field : type.getDeclaredFields()) {
    if (isProtected(field.getModifiers()) || isPublic(field.getModifiers())) {
       inheritableFields.add(field);
    }
}
3
Marek Dec
private static void addDeclaredAndInheritedFields(Class<?> c, Collection<Field> fields) {
    fields.addAll(Arrays.asList(c.getDeclaredFields())); 
    Class<?> superClass = c.getSuperclass(); 
    if (superClass != null) { 
        addDeclaredAndInheritedFields(superClass, fields); 
    }       
}

Version de travail de la solution "DidYouMeanThatTomHa ..." ci-dessus

2
Theo Platt

Plus court et avec moins d'objets instanciés? ^^

private static Field[] getAllFields(Class<?> type) {
    if (type.getSuperclass() != null) {
        return (Field[]) ArrayUtils.addAll(getAllFields(type.getSuperclass()), type.getDeclaredFields());
    }
    return type.getDeclaredFields();
}
1
Alexis LEGROS

Tu peux essayer:

   Class parentClass = getClass().getSuperclass();
   if (parentClass != null) {
      parentClass.getDeclaredFields();
   }
1
Manuel Selva

J'ai récemment vu ce code de org.Apache.commons.lang3.reflect.FieldUtils

public static List<Field> getAllFieldsList(final Class<?> cls) {
        Validate.isTrue(cls != null, "The class must not be null");
        final List<Field> allFields = new ArrayList<>();
        Class<?> currentClass = cls;
        while (currentClass != null) {
            final Field[] declaredFields = currentClass.getDeclaredFields();
            Collections.addAll(allFields, declaredFields);
            currentClass = currentClass.getSuperclass();
        }
        return allFields;
}
0
aman
private static void addDeclaredAndInheritedFields(Class c, Collection<Field> fields) {
    fields.addAll(Arrays.asList(c.getDeclaredFields()));
    Class superClass = c.getSuperclass();
    if (superClass != null) {
        addDeclaredAndInheritedFields(superClass, fields);
    }
}

Ceci est une reformulation de la réponse acceptée par @ user1079877. Il se peut qu’une version qui ne modifie pas un paramètre de la fonction et utilise également certaines fonctionnalités Java modernes.

public <T> Field[] getFields(final Class<T> type, final Field... fields) {
    final Field[] items = Stream.of(type.getDeclaredFields(), fields).flatMap(Stream::of).toArray(Field[]::new);
    if (type.getSuperclass() == null) {
        return items;
    } else {
        return getFields(type.getSuperclass(), items);
    }
}

Cette implémentation rend également l’invocation un peu plus concise:

var fields = getFields(MyType.class);
0
scrutari