使用Java Reflection检索继承的属性名称/值


128

我有一个Java对象'ChildObj',它是从'ParentObj'扩展而来的。现在,是否可以使用Java反射机制来检索ChildObj的所有属性名称和值,包括继承的属性?

Class.getFields为我提供了公共属性的数组,而Class.getDeclaredFields为我提供了所有字段的数组,但是它们都不包含继承的字段列表。

还有什么方法可以检索继承的属性?

Answers:


173

不,您需要自己编写。这是一个简单的递归方法,称为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));
}

2
是。想到了。但想检查是否还有其他方法可以做到这一点。谢谢。:)
Veera

7
传递可变的参数并返回它可能不是一个很好的设计。fields.addAll(type.getDeclaredFields()); 比使用add的增强型for循环更传统。
Tom Hawtin-大头钉

我认为至少需要对其进行编译(在stackoverflow上!),并可能添加一些Arrays.asList。
Tom Hawtin-大头钉

看来您的代码收集了所有字段,还包括未继承的私有字段和静态字段。
彼得·韦尔哈斯

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

9
这是我的首选解决方案,但是我将其称为“ getAllFields”,因为它也返回给定类的字段。
2012年

3
尽管我非常喜欢递归(这很有趣!),但我更喜欢这种方法的可读性和更直观的参数(不需要传递新的集合),不再需要if(在for子句中隐含)并且没有对字段的迭代他们自己。
Remi Morin 2013年

它表明递归是不必要的。..我喜欢短代码!谢谢!:)
水瓶座力量

多年以来,我一直认为for的初始值只是一个整数,对于@Veera的问题,我认为只有递归才能解决它,@ Esko Luontola您的命令很棒。
Touya Akira

@Esko:非常感谢。保存了一天!简洁且完美无缺!
gaurav

37

相反,如果您想依靠库来完成此任务,则Apache Commons Lang版本3.2+提供了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));
    }
}

6
繁荣!我爱不重塑方向盘。为此加油。
约书亚·品特

6

您需要致电:

Class.getSuperclass().getDeclaredFields()

根据需要递归继承层次结构。


5

使用反射库:

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

4

递归解决方案还可以,唯一的小问题是它们返回已声明成员和继承成员的超集。请注意,getDeclaredFields()方法还会返回私有方法。因此,假设您浏览了整个超类层次结构,则将包括在超类中声明的所有私有字段,而这些私有字段不会被继承。

一个带有Modifier.isPublic ||的简单过滤器 Modifier.isProtected谓词将执行以下操作:

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

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

上面的“ DidYouMeanThatTomHa ...”解决方案的工作版本


2

使用spring util库,您可以用来检查类中是否存在一个特定的属性:

Field field = ReflectionUtils.findRequiredField(YOUR_CLASS.class, "ATTRIBUTE_NAME");

log.info(field2.getName());

api文档:
https : //docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/util/ReflectionUtils.html

要么

 Field field2 = ReflectionUtils.findField(YOUR_CLASS.class, "ATTRIBUTE_NAME");

 log.info(field2.getName());

api文档:
https : //docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/util/ReflectionUtils.html

@干杯



1

实例化的对象更短,对象更少?^^

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

嗨@Alexis LEGROS:ArrayUtils找不到符号。
Touya Akira

1
此类来自Apache Commons Lang。
亚历克西斯·莱戈斯

Apache已经具有FieldUtils.getAllFields函数来处理此问题请求。
Touya Akira

1

getFields(): 获取整个类层次结构中的所有公共字段,以及
getDeclaredFields(): 获取所有字段,无论它们的修饰符如何,但仅针对当前类。因此,您必须获取所有涉及的层次结构。
我最近从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
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);
    }
}

0

这是@ user1079877接受的答案的改写。一个版本可能不修改该函数的参数,而是使用一些现代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);
    }
}

此实现还使调用更加简洁:

var fields = getFields(MyType.class);

0

FieldUtils并没有解决一些奇怪的问题-特别是合成字段(例如,由JaCoCo注入),当然,枚举类型当然为每个实例都有一个字段,并且如果您遍历对象图,所有字段,然后获取每个字段的字段,等等,然后您在遇到枚举时将陷入无限循环。一个扩展的解决方案(老实说,我确定这必须存在于某个地方的库中!)将是:

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

Spock中的测试类(Groovy添加了合成字段):

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

    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.