I've a Java object 'ChildObj' which is extended from 'ParentObj'. Now, if it is possible to retrieve all the attribute names and values of ChildObj, including the inherited attributes too, using Java reflection mechanism?
Class.getFields gives me the array of public attributes, and Class.getDeclaredFields gives me the array of all fields, but none of them includes the inherited fields list.
Is there any way to retrieve the inherited attributes also?
15 Answers 15
no, you need to write it yourself. It is a simple recursive method called on 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));
}
-
8Passing an mutable argument in and returning it probably isn't a great design. fields.addAll(type.getDeclaredFields()); would be more conventional than a enhanced for loop with add.Tom Hawtin - tackline– Tom Hawtin - tackline06/25/2009 10:11:51Commented Jun 25, 2009 at 10:11
-
I'd feel the need to at least compile it (on stackoverflow!), and probably add in a little Arrays.asList.Tom Hawtin - tackline– Tom Hawtin - tackline06/25/2009 10:34:05Commented Jun 25, 2009 at 10:34
-
It seems that your code collects all the fields, also private and static fields that are not inherited.Peter Verhas– Peter Verhas02/12/2019 13:56:28Commented Feb 12, 2019 at 13:56
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;
}
-
5Although I like very much recursivity (it's fun!), I prefer the readability of this method and the more intuitive parameters (not required a new collection to be pass), no more if (implicit in the for clause) and no iteration over fields themselves.Remi Morin– Remi Morin09/10/2013 13:22:32Commented Sep 10, 2013 at 13:22
-
1it shows recursive is unnecessary and.. I like short codes! thx! :)Aquarius Power– Aquarius Power03/04/2015 08:08:03Commented Mar 4, 2015 at 8:08
-
1In many years i always think the initial value in for is just an integer, with @Veera's question I think only recursive can solve it,@Esko Luontola your command is awesome.Touya Akira– Touya Akira09/09/2017 10:20:31Commented Sep 9, 2017 at 10:20
If instead you wanted to rely upon a library to accomplish this, Apache Commons Lang version 3.2+ provides 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));
}
}
You need to call:
Class.getSuperclass().getDeclaredFields()
Recursing up the inheritance hierarchy as necessary.
getFields():
Gets all the public fields up the entire class hierarchy and
getDeclaredFields():
Gets all the fields, regardless of their modifiers but only for the current class. So, you have to get for all the hierarchy involved.
I recently saw this code from 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;
}
-
1Its better than best answer, because while cycle always faster than recursive calls. But i think LinkedList as data structure more suitable instead of ArrayList, because ArraysList will be resized (a few times), when size of list reaches capacity value, what will entail time costsAlex– Alex03/03/2021 11:15:55Commented Mar 3, 2021 at 11:15
Use Reflections library:
public Set<Field> getAllFields(Class<?> aClass) {
return org.reflections.ReflectionUtils.getAllFields(aClass);
}
With spring util library, you can use to check if one specific attribute exists into class:
Field field = ReflectionUtils.findRequiredField(YOUR_CLASS.class, "ATTRIBUTE_NAME");
log.info(field2.getName());
or
Field field2 = ReflectionUtils.findField(YOUR_CLASS.class, "ATTRIBUTE_NAME");
log.info(field2.getName());
@cheers
The recursive solutions are OK, the only small issue is that they return a superset of declared and inherited members. Note that getDeclaredFields() method returns also private methods. So given that you navigate the whole superclass hierarchy you will include all private fields declared in the superclasses, and those don't get inherited.
A simple filter with a Modifier.isPublic || Modifier.isProtected predicate would do:
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);
}
}
Working version of "DidYouMeanThatTomHa..." solution above
You can try:
Class parentClass = getClass().getSuperclass();
if (parentClass != null) {
parentClass.getDeclaredFields();
}
Shorter and with less object instantiated ? ^^
private static Field[] getAllFields(Class<?> type) {
if (type.getSuperclass() != null) {
return (Field[]) ArrayUtils.addAll(getAllFields(type.getSuperclass()), type.getDeclaredFields());
}
return type.getDeclaredFields();
}
-
HI @Alexis LEGROS : ArrayUtils cannot find symbol.Touya Akira– Touya Akira09/09/2017 00:29:16Commented Sep 9, 2017 at 0:29
-
1This class is from Apache Commons Lang.Alexis LEGROS– Alexis LEGROS09/13/2017 07:31:19Commented Sep 13, 2017 at 7:31
-
Apache already has a FieldUtils.getAllFields function to handle this question request.Touya Akira– Touya Akira09/13/2017 09:23:37Commented Sep 13, 2017 at 9:23
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);
}
}
This is a rewording of the accepted answer by @user1079877. It might that a version which does not modify a parameter of the function and also uses some modern Java features.
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);
}
}
This implementation also makes invocation a bit more concise:
var fields = getFields(MyType.class);
There are a couple of quirks that aren't addressed by FieldUtils - specifically synthetic fields (eg injected by JaCoCo) and also the fact that an enum type of course has a field for each instance, and if you are traversing an object graph, getting all fields and then getting the fields of each of them etc, then you will get into an infinite loop when you hit an enum. An extended solution (and to be honest I'm sure this must live in a library somewhere!) would be:
/**
* 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);
}
Test class in Spock (and Groovy adds synthetic fields):
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
}
}
I know this is a long overdue answer, but I just put here my answer just for my reference or anyone who is interested about an implementation without reflection as an extension of @dfa's answer;
public List<Field> getDeclaredFields(Class<?> tClass) {
List<Field> fields = new LinkedList<>();
while (tClass != null) {
fields.addAll(Arrays.asList(tClass.getDeclaredFields()));
tClass = tClass.getSuperclass();
}
return fields;
}