如何使用反射获取嵌套类属性的值?

时间:2016-02-24 17:51:46

标签: java reflection

目前我有几个嵌套类的类,如:

public class Foo {

  private Bar bar;

  //getter & setter

  public class Bar {

    private Abc abc;

    //getter & setter

    public class Abc {
      @RequiredParam
      private String property;

      //getter & setter
    }
  }

}

我试图获得字段的价值,但我很难实现这个目标。

到目前为止,我有:

  public static boolean isValid(Object paramClazz) throws Exception {
    List<Class> classes = new ArrayList<>();
    getClasses(classes, paramClazz.getClass());

    for (Class clazz : classes) {
      for (Field field : clazz.getDeclaredFields()) {
        if (field.isAnnotationPresent(RequiredParam.class)) {
          field.setAccessible(true);
          //how to get the value? field.get(paramClazz) doesn't work
        }
      }
    }
    return true;
  }

  private static void getClasses(List<Class> classes, Class<?> clazz) {
    if (clazz.getDeclaredClasses().length > 0) {
      for (Class<?> c : clazz.getDeclaredClasses()) {
        getClasses(classes, c);
      }
    }
    classes.add(clazz);
  }

我的目标是能够检查用@RequiredParam注释的字段是否为空,所以我有方法isValid(),它将接收一个对象,并且应该能够检查所有字段(甚至是里面的字段)嵌套类)并查看是否有任何缺失。

问题是当我尝试拨打field.get()并且我不知道我应该将哪个对象传递给此方法时。传递最高级别的对象不会起作用,因为我只需要以某种方式将Abc对象传递给方法。

我怎样才能将正确的对象传递给field.get()调用,考虑到我的类中可以有更多或更少的嵌套级别?

1 个答案:

答案 0 :(得分:3)

这是一个示例代码,它以递归方式扫描对象的所有字段,直到找到所有带注释字段的值:

public static Collection<Object> getAnnotatedValues(final Object root) throws ReflectiveOperationException {
    return getAnnotatedValues(root, new HashSet<>());
}

private static Collection<Object> getAnnotatedValues(final Object root, final Set<Object> inspected)
        throws ReflectiveOperationException {
    final List<Object> annotatedValues = new ArrayList<>();
    if (inspected.contains(root)) { // Prevents stack overflow.
        return Collections.EMPTY_LIST;
    }
    inspected.add(root);
    for (final Field field : gatherFields(root.getClass())) {
        field.setAccessible(true);
        final Object currentValue = field.get(root);
        field.setAccessible(false);
        if (field.isAnnotationPresent(RequiredParam.class)) {
            // Found required value, search finished:
            annotatedValues.add(currentValue);
            if (currentValue != null) {
                inspected.add(currentValue);
            }
        } else if (currentValue != null) {
            // Searching for annotated fields in nested classes:
            annotatedValues.addAll(getAnnotatedValues(currentValue, inspected));
        }
    }
    return annotatedValues;
}

private static Iterable<Field> gatherFields(Class<?> fromClass) {
    // Finds ALL fields, even the ones from super classes.
    final List<Field> fields = new ArrayList<>();
    while (fromClass != null) {
        fields.addAll(Arrays.asList(fromClass.getDeclaredFields()));
        fromClass = fromClass.getSuperclass();
    }
    return fields;
}

您可能已经实现了类似的功能,但无法访问最后一个嵌套类。这是因为Field实例只是类的描述并且(除非该字段是静态的)它需要一个能够提取值的实际实例。来自Field#get(Object)方法文档:

  

如果底层字段是静态字段,则忽略obj参数;它可能是空的。

     

否则,基础字段是实例字段。如果指定的obj参数为null,则该方法抛出NullPointerException。如果指定的对象不是声明基础字段的类或接口的实例,则该方法将抛出IllegalArgumentException。

如果你没有在类结构中启动字段,那么你很难提取一个值 - 即使你找到了注释字段,你仍然需要一个类的实例来提取它们。例如,给定这些类:

public class Foo {
    private final Bar bar = new Bar();
    public class Bar {
        private final Abc abc = new Abc();
        public class Abc {
            @RequiredParam private final String property = "Result.";
        }
    }
    @Retention(RetentionPolicy.RUNTIME)
    public static @interface RequiredParam {}
}

... getAnnotatedValues(new Foo())返回包含"Result."的集合。您可以轻松修改方法以满足您的需求(例如,一旦找到第一个有效的注释字段就返回true,或者如果集合为空/只包含空值则返回false)。