【问题标题】:Retrieving the inherited attribute names/values using Java Reflection使用 Java 反射检索继承的属性名称/值
【发布时间】:2009-06-25 09:11:21
【问题描述】:

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

Class.getFields 给了我公共属性的数组,Class.getDeclaredFields 给了我所有字段的数组,但没有一个包含继承的字段列表。

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

【问题讨论】:

    标签: java reflection introspection


    【解决方案1】:

    不,你需要自己写。这是一个在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));
    }
    

    【讨论】:

    • 传入一个可变参数并返回它可能不是一个好的设计。 fields.addAll(type.getDeclaredFields());将比带有 add 的增强 for 循环更传统。
    • 我觉得至少需要编译它(在 stackoverflow 上!),并且可能添加一点 Arrays.asList。
    • 您的代码似乎收集了所有字段,还有未继承的私有和静态字段。
    【解决方案2】:
        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;
        }
    

    【讨论】:

    • 这是我的首选解决方案,但我将其称为“getAllFields”,因为它也返回给定类的字段。
    • 虽然我非常喜欢递归性(这很有趣!),但我更喜欢这种方法的可读性和更直观的参数(不需要传递新集合),而不是 if(隐含在for 子句)并且不会对字段本身进行迭代。
    • 它表明递归是不必要的,而且.. 我喜欢短代码!谢谢! :)
    • 多年来我一直认为 for 中的初始值只是一个整数,@Veera 的问题我认为只有递归才能解决它,@Esko Luontola 你的命令很棒。
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      您需要致电:

      Class.getSuperclass().getDeclaredFields()
      

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

      【讨论】:

        【解决方案5】:

        使用反射库:

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

        【讨论】:

          【解决方案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;
          }
          

          【讨论】:

          • 它比最佳答案更好,因为 while 循环总是比递归调用快。但我认为 LinkedList 作为数据结构比 ArrayList 更合适,因为 ArraysList 会被调整大小(几次),当列表的大小达到容量值时,会带来什么时间成本
          【解决方案7】:

          递归解决方案没问题,唯一的小问题是它们返回声明和继承成员的超集。请注意,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);
              }
          }
          

          【讨论】:

            【解决方案8】:

            使用 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

            @cheers

            【讨论】:

              【解决方案9】:
              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...”解决方案的工作版本

              【讨论】:

                【解决方案10】:

                你可以试试:

                   Class parentClass = getClass().getSuperclass();
                   if (parentClass != null) {
                      parentClass.getDeclaredFields();
                   }
                

                【讨论】:

                  【解决方案11】:

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

                  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 找不到符号。
                  • 本课程来自 Apache Commons Lang。
                  • Apache 已经有一个 FieldUtils.getAllFields 函数来处理这个问题请求。
                  【解决方案12】:
                  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);
                      }
                  }
                  

                  【讨论】:

                    【解决方案13】:

                    这是对@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);
                    

                    【讨论】:

                      【解决方案14】:

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

                      【讨论】:

                        【解决方案15】:

                        我知道这是一个姗姗来迟的答案,但我只是把我的答案放在这里,仅供参考,或者任何对没有反射的实现感兴趣的人作为@dfa 答案的扩展;

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

                        【讨论】:

                          猜你喜欢
                          • 2011-03-19
                          • 1970-01-01
                          • 2011-10-02
                          • 1970-01-01
                          • 1970-01-01
                          • 2010-09-17
                          • 2019-02-02
                          • 2012-02-05
                          • 1970-01-01
                          相关资源
                          最近更新 更多