Currently I have classes with several nested classes inside is, like:
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
}
}
}
Run Code Online (Sandbox Code Playgroud)
I am trying to get the value of the fields but I am having a hard time how to achieve this.
So far I have:
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);
}
Run Code Online (Sandbox Code Playgroud)
My goal is to the able to check if the field annotated with @RequiredParam is not null, so I have the method isValid() which will received an object and should be able to check all fields (even the ones inside nested classes) and see if any is missing.
The problem is when I try to call field.get() and I don't know which object I am supposed to pass to this method. Passing the highest level object won't work, because I need somehow to pass only the Abc object to the method.
field.get()考虑到我的类中可以有或多或少的嵌套级别,我如何才能将正确的对象传递给调用?
这是一个示例代码,它递归地扫描对象的所有字段,直到找到所有带注释的字段的值:
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;
}
Run Code Online (Sandbox Code Playgroud)
您可能已经实现了类似的东西,但无法进入最后一个嵌套类。这是因为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 {}
}
Run Code Online (Sandbox Code Playgroud)
...getAnnotatedValues(new Foo())返回包含"Result.". 您可以轻松修改方法以满足您的需要(例如,true一旦找到第一个有效的带注释的字段就返回,或者如果集合为空/包含空值则简单地返回 false)。