当前位置: 代码迷 >> java >> Java - 使用反射递归修改对象值
  详细解决方案

Java - 使用反射递归修改对象值

热度:101   发布时间:2023-07-17 20:42:19.0

我想转换对象的每个 String 属性(及其嵌套对象),并且我使用以下递归方法通过反射 API 来实现:

    public static void reflect(Object obj) {
        if (obj == null) {
            return;
        }
        Class klazz = obj.getClass();
        if (klazz.isPrimitive()
                || obj instanceof Integer
                || obj instanceof Double
                || obj instanceof Boolean)
            return;
        else {
            try {
                for (Field field : klazz.getDeclaredFields()) {
                    field.setAccessible(true);
                    Object f = field.get(obj);
                    if(f instanceof String) {
                        f = transform(f);
                        field.set(obj, f);
                    }
                    else {
                        reflect(f);
                    }
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            }
        }
    }

    private static Object transform(Object f) {
        f = f + "blabla";
        return f;
    }


@Data
@Builder
public class PrintObject {
    private String field1;
    private String field2;
    private String field3;
    private NestedObject field4;
}

@Data
@Builder
public class NestedObject {
    private String field1;
    private String field2;
    private Integer field3;
}

NestedObject nestedObject = NestedObject
                .builder()
                .field1("test")
                .field2("test2")
                .field3(1)
                .build();

PrintObject printObject = PrintObject
      .builder()
      .field1("test")
      .field2("Test")
      .field3("test")
      .field4(nestedObject)
      .build();

Utils.reflect(printObject);

到目前为止,一切正常,如果我执行此操作,则所有字符串值最后都会附加“blabla”。 如果 PrintObject 具有其他数据结构(如 List 或 Map),则会出现问题。 例如,如果 PrintObject 类中有另一个字段:

private List<String> field5;

那么这段代码执行会抛出 StackOverflowError。

List<String> list = new ArrayList<>();
list.add("test");

NestedObject nestedObject = NestedObject
                .builder()
                .field1("test")
                .field2("test2")
                .field3(1)
                .build();

PrintObject printObject = PrintObject
      .builder()
      .field1("test")
      .field2("Test")
      .field3("test")
      .field4(nestedObject)
      .field5(list)
      .build();

Utils.reflect(printObject);

关于如何使用这些结构进行这项工作的任何想法? 提前致谢。

field5 也可以是例如:

 Map<String,String>

甚至

 List<List<String>>

ArrayList包含一个long serialVersionUID字段以帮助序列化。 当您获得该值时,它会返回一个装箱的Long Long上调用getDeclaredFields返回一个包含Long.MIN_VALUE字段的数组,它是一个Long 这就是无限循环的由来。

为了解决这个问题,我会像你对Integer一样为Long添加特殊情况处理。 您还应该考虑所有其他盒装原语,如FloatByte

集合将由相互引用链接LinkedList或由数组支持。 对于链接结构,代码将遍历它们。 要支持数组支持收集,您需要确定哪些字段是数组并遍历它们。

字段的类型,可通过。 数组可以通过来标识。 不同类型的数组具有不同的类型,它们不像 Java 泛型那样是非具体化的。 非原始值的数组可以转换为Object[] ,这在这种情况下很有用,但它。 可以使用来获取数组中对象的类型。

需要像下面这样的东西来递归数组的条目。

final Class<?> fieldType = field.getType();
if (fieldType.isArray() && !fieldType.getComponentType().isPrimitive()) {
    Object[] fs = (Object[]) f;
    for (Object fi : fs) {
        reflect(fi);
    }
}

另一个问题是循环引用可能导致进一步的StackOverflowException 如果将列表作为成员添加到自身,它将无限递归。 有必要跟踪以前访问过的对象,而不是两次访问它们。 理想情况下,这将使用IdentityHashMap因为您关心对象的实例而不是它们的相等性。

  相关解决方案