Me gustaría transformar cada propiedad String de un objeto (junto con sus objetos anidados) y estoy usando el siguiente método recursivo para lograr que con la API de reflexión:
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);
Hasta este punto, todos los trabajos muy bien y si yo haga esto, entonces todos los valores de cadena se añade "blabla" al final. El problema viene si PrintObject tiene otras estructuras de datos como lista o mapa. Por ejemplo, si hay otro campo en la clase PrintObject:
private List<String> field5;
entonces esto ejecución de código tiraría 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);
Alguna idea sobre cómo hacer este trabajo con estas estructuras, así? Gracias por adelantado.
Field5 también podría ser por ejemplo:
Map<String,String>
o incluso
List<List<String>>
ArrayList
contiene un long
serialVersionUID
campo para ayudar con la serialización. Cuando se obtiene el valor que devuelve una caja Long
. Llamada getDeclaredFields
en Long
devuelve una matriz que contiene el campo Long.MIN_VALUE
que es una Long
. Ahí es donde el bucle infinito viene.
Para resolverlo yo añadiría un manejo especial para el caso Long
como lo hace para Integer
. También debe considerar todas las otras primitivas como en caja Float
y Byte
.
Colecciones serán respaldados, ya sea por las estructuras que se refieren el uno al otro enlace LinkedList
o mediante matrices. Para estructuras vinculadas el código atravesaría ellos. Con el respaldo de matriz recoge el apoyo que necesita para identificar qué campos son matrices e iterar sobre ellos.
El tipo de un campo y se obtiene con Field.getType . Las matrices pueden ser identificados por Class.isArray . Las matrices para diferentes tipos tienen diferentes tipos, no son no materializadas como genéricos de Java. Matrices de valores no primitivos se puede convertir a Object[]
que es útil en este caso, pero que es no un tipo seguro . Para obtener el tipo de objeto de la matriz Class.getComponentType se puede utilizar.
Se necesitaría algo como lo siguiente a recursiva en las entradas de una matriz.
final Class<?> fieldType = field.getType();
if (fieldType.isArray() && !fieldType.getComponentType().isPrimitive()) {
Object[] fs = (Object[]) f;
for (Object fi : fs) {
reflect(fi);
}
}
El otro problema es referencias cíclicos que podrían causar aún más StackOverflowException
. Si una lista se agrega como miembro a sí mismo sería infinitamente recursiva. Es necesario realizar un seguimiento de objetos visitado previamente y no visitar dos veces. Lo ideal sería utilizar una IdentityHashMap
medida que se preocupan por las instancias de objetos no su igualdad.