使用Java反射获取继承属性的名称/值

165

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

Class.getFields 给我公共属性数组,Class.getDeclaredFields 给我所有字段数组,但它们都不包括继承字段列表。

有没有办法检索继承的属性呢?

15个回答

222

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

8
将可变参数作为参数传入并返回它可能不是一个很好的设计。使用fields.addAll(type.getDeclaredFields());会比使用增强型for循环和add更加常规化。 - Tom Hawtin - tackline
我会感觉有必要至少在stackoverflow上编译它,并且可能会添加一点Arrays.asList。 - Tom Hawtin - tackline
看起来你的代码收集了所有字段,包括私有和静态字段,但不包括继承的字段。 - Peter Verhas

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

10
这是我的首选解决方案,不过我会称其为“getAllFields”,因为它也会返回给定类的字段。 - Pino
5
虽然我非常喜欢递归(很有趣!),但我更喜欢这种方法的可读性和更直观的参数(不需要传递新集合),不再需要if语句(隐含在for子句中),也不需要对字段本身进行迭代。 - Remi Morin
1
它显示递归是不必要的,我喜欢短代码!谢谢! :) - Aquarius Power
1
多年来,我一直认为for循环中的初始值只是一个整数。通过@Veera的问题,我认为只有递归才能解决这个问题。@Esko Luontola,你的命令真棒。 - Touya Akira

45

如果您想依赖库来完成此操作,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));
    }
}

9

您需要调用:

Class.getSuperclass().getDeclaredFields()

必要时,递归向上继承层次结构。

6

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

1
这比最佳答案更好,因为循环总是比递归调用快。但我认为链表作为数据结构比ArrayList更合适,因为当列表大小达到容量值时,ArraysList将被重新调整大小(几次),这将带来时间成本。 - Alex

5

使用Reflections库:

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

4

使用Spring工具库,您可以检查一个特定属性是否存在于类中:

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

@cheers


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..." 解决方案的工作版本


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。 - Alexis LEGROS
Apache已经有了FieldUtils.getAllFields函数来处理这个问题请求。 - Touya Akira

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接