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?
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));
}
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;
}
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));
}
}
Vous devez appeler:
Class.getSuperclass().getDeclaredFields()
Récursant la hiérarchie d'héritage si nécessaire.
Utilisez la bibliothèque de réflexions:
public Set<Field> getAllFields(Class<?> aClass) {
return org.reflections.ReflectionUtils.getAllFields(aClass);
}
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);
}
}
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
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();
}
Tu peux essayer:
Class parentClass = getClass().getSuperclass();
if (parentClass != null) {
parentClass.getDeclaredFields();
}
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;
}
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);