Java - рекурсивно изменять значения объекта с помощью отражения - PullRequest
0 голосов
/ 25 февраля 2019

Я хотел бы преобразовать каждое свойство 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);

До этого момента каждыйработает нормально, и если я выполню это, то все значения String будут добавлены с "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>>

1 Ответ

0 голосов
/ 25 февраля 2019

ArrayList содержит поле long serialVersionUID, чтобы помочь с сериализацией.Когда вы получаете значение, оно возвращает в штучной упаковке Long.Вызов getDeclaredFields on Long возвращает массив, содержащий поле Long.MIN_VALUE, которое является Long.Отсюда и бесконечный цикл.

Чтобы разрешить его, я бы добавил специальную обработку для Long, как вы делаете для Integer.Вы также должны учитывать все другие примитивы в штучной упаковке, такие как Float и Byte.

Коллекции будут поддерживаться либо структурами, которые ссылаются друг на друга LinkedList, либо массивами.Для связанных структур код будет проходить через них.Для поддержки собранных массивов необходимо определить, какие поля являются массивами, и выполнить итерации по ним.

Тип поля и получить его с помощью Field.getType .Массивы можно идентифицировать с помощью Class.isArray .Массивы для разных типов имеют разные типы, они не являются нереализованными, как дженерики Java.Массивы непримитивных значений могут быть преобразованы в Object[], что полезно в этом случае, но оно не является безопасным для типа .Чтобы получить тип объекта в массиве, можно использовать Class.getComponentType .

Чтобы выполнить рекурсивный анализ элементов массива, потребуется что-то вроде следующего.

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

Другая проблема - циклические ссылки, которые могут вызвать дальнейшее StackOverflowException.Если бы список был добавлен в качестве члена к себе, он будет повторяться бесконечно.Необходимо отслеживать ранее посещенный объект и не посещать их дважды.В идеале это будет использовать IdentityHashMap, так как вы заботитесь о экземплярах объектов, а не об их равенстве.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...