Mam obiekt Java „ChildObj”, który jest rozszerzony z „ParentObj”. Teraz, czy możliwe jest pobranie wszystkich nazw atrybutów i wartości ChildObj, w tym również atrybutów dziedziczonych, przy użyciu mechanizmu odbicia Java?
Class.getFields podaje tablicę atrybutów publicznych, a Class.getDeclaredFields - tablicę wszystkich pól, ale żadne z nich nie zawiera listy dziedziczonych pól.
Czy istnieje sposób na odzyskanie również odziedziczonych atrybutów?
java
reflection
introspection
Veera
źródło
źródło
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; }
źródło
Jeśli zamiast tego chcesz polegać na bibliotece, aby to osiągnąć, Apache Commons Lang w wersji 3.2+ zapewnia
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)); } }
źródło
Musisz zadzwonić:
W razie potrzeby cykliczne w górę hierarchii dziedziczenia.
źródło
Użyj biblioteki refleksji:
public Set<Field> getAllFields(Class<?> aClass) { return org.reflections.ReflectionUtils.getAllFields(aClass); }
źródło
Rozwiązania rekurencyjne są w porządku, jedynym małym problemem jest to, że zwracają nadzbiór zadeklarowanych i dziedziczonych członków. Zauważ, że metoda getDeclaredFields () zwraca również metody prywatne. Biorąc więc pod uwagę, że poruszasz się po całej hierarchii nadklas, uwzględnisz wszystkie prywatne pola zadeklarowane w nadklasach, a te nie są dziedziczone.
Prosty filtr z modyfikatorem.isPublic || Modifier.isProtected predykat zrobiłby:
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); } }
źródło
Dzięki bibliotece Spring util możesz użyć, aby sprawdzić, czy w klasie istnieje jeden określony atrybut:
Field field = ReflectionUtils.findRequiredField(YOUR_CLASS.class, "ATTRIBUTE_NAME"); log.info(field2.getName());
Dokument API:
https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/util/ReflectionUtils.html
lub
Field field2 = ReflectionUtils.findField(YOUR_CLASS.class, "ATTRIBUTE_NAME"); log.info(field2.getName());
Dokument API:
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/util/ReflectionUtils.html
@Twoje zdrowie
źródło
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); } }
Działająca wersja rozwiązania „DidYouMeanThatTomHa ...” powyżej
źródło
Możesz spróbować:
Class parentClass = getClass().getSuperclass(); if (parentClass != null) { parentClass.getDeclaredFields(); }
źródło
Krótszy i z mniejszą liczbą instancji obiektów? ^^
private static Field[] getAllFields(Class<?> type) { if (type.getSuperclass() != null) { return (Field[]) ArrayUtils.addAll(getAllFields(type.getSuperclass()), type.getDeclaredFields()); } return type.getDeclaredFields(); }
źródło
getFields (): Pobiera wszystkie pola publiczne w górę całej hierarchii klas i
getDeclaredFields (): Pobiera wszystkie pola, niezależnie od ich modyfikatorów, ale tylko dla bieżącej klasy. Więc musisz wziąć pod uwagę całą zaangażowaną hierarchię.
Niedawno widziałem ten kod z 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; }
źródło
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); } }
źródło
To jest przeformułowanie zaakceptowanej odpowiedzi przez @ user1079877. Może się zdarzyć, że wersja, która nie modyfikuje parametru funkcji, a także korzysta z niektórych nowoczesnych funkcji Java.
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); } }
Ta implementacja sprawia również, że wywołanie jest nieco bardziej zwięzłe:
var fields = getFields(MyType.class);
źródło
Istnieje kilka dziwactw, które nie są uwzględniane przez FieldUtils - w szczególności pola syntetyczne (np. Wstrzykiwane przez JaCoCo), a także fakt, że typ kursu wyliczeniowego ma pole dla każdej instancji, a jeśli przechodzisz przez wykres obiektu, otrzymujesz wszystkie pola, a następnie otrzymujesz pola każdego z nich itp., a następnie uzyskasz nieskończoną pętlę, gdy trafisz na wyliczenie. Rozszerzone rozwiązanie (i szczerze mówiąc, na pewno musi mieszkać gdzieś w bibliotece!) To:
/** * Return a list containing all declared fields and all inherited fields for the given input * (but avoiding any quirky enum fields and tool injected fields). */ public List<Field> getAllFields(Object input) { return getFieldsAndInheritedFields(new ArrayList<>(), input.getClass()); } private List<Field> getFieldsAndInheritedFields(List<Field> fields, Class<?> inputType) { fields.addAll(getFilteredDeclaredFields(inputType)); return inputType.getSuperclass() == null ? fields : getFieldsAndInheritedFields(fields, inputType.getSuperclass()); } /** * Where the input is NOT an {@link Enum} type then get all declared fields except synthetic fields (ie instrumented * additional fields). Where the input IS an {@link Enum} type then also skip the fields that are all the * {@link Enum} instances as this would lead to an infinite loop if the user of this class is traversing * an object graph. */ private List<Field> getFilteredDeclaredFields(Class<?> inputType) { return Arrays.asList(inputType.getDeclaredFields()).stream() .filter(field -> !isAnEnum(inputType) || (isAnEnum(inputType) && !isSameType(field, inputType))) .filter(field -> !field.isSynthetic()) .collect(Collectors.toList()); } private boolean isAnEnum(Class<?> type) { return Enum.class.isAssignableFrom(type); } private boolean isSameType(Field input, Class<?> ownerType) { return input.getType().equals(ownerType); }
Klasa testowa w Spocku (a Groovy dodaje pola syntetyczne):
class ReflectionUtilsSpec extends Specification { def "declared fields only"() { given: "an instance of a class that does not inherit any fields" def instance = new Superclass() when: "all fields are requested" def result = new ReflectionUtils().getAllFields(instance) then: "the fields declared by that instance's class are returned" result.size() == 1 result.findAll { it.name in ['superThing'] }.size() == 1 } def "inherited fields"() { given: "an instance of a class that inherits fields" def instance = new Subclass() when: "all fields are requested" def result = new ReflectionUtils().getAllFields(instance) then: "the fields declared by that instance's class and its superclasses are returned" result.size() == 2 result.findAll { it.name in ['subThing', 'superThing'] }.size() == 2 } def "no fields"() { given: "an instance of a class with no declared or inherited fields" def instance = new SuperDooperclass() when: "all fields are requested" def result = new ReflectionUtils().getAllFields(instance) then: "the fields declared by that instance's class and its superclasses are returned" result.size() == 0 } def "enum"() { given: "an instance of an enum" def instance = Item.BIT when: "all fields are requested" def result = new ReflectionUtils().getAllFields(instance) then: "the fields declared by that instance's class and its superclasses are returned" result.size() == 3 result.findAll { it.name == 'smallerItem' }.size() == 1 } private class SuperDooperclass { } private class Superclass extends SuperDooperclass { private String superThing } private class Subclass extends Superclass { private String subThing } private enum Item { BIT("quark"), BOB("muon") Item(String smallerItem) { this.smallerItem = smallerItem } private String smallerItem } }
źródło