2012-03-09 16 views
12

Estoy tratando de validar serializar y deserializar rutinas comparando el objeto resultante con el objeto original. Las rutinas pueden serializar clases arbitrarias y profundamente anidadas y, en consecuencia, quiero una rutina de comparación a la que se le pueda dar la instancia original y final y recorrer reflexivamente cada tipo de valor y comparar los valores y bucear iterativamente en tipos de referencia para comparar valores.profundo de reflexión es igual a comparar

He probado el Apache Commons Lang EqualsBuilder.reflectionEquals(inst1, inst2) pero esto no parece hacer una comparación muy profunda, que se limita a comparar los tipos de referencia para la igualdad en lugar de buceo más profundo en ellos:

El código siguiente ilustra mi problema. La primera llamada al reflectionEquals devuelve verdadero pero la segunda devuelve falso.

¿Existe una rutina de biblioteca alguien podría recomendar?

class dummy { 
    dummy2 nestedClass; 
} 

class dummy2 { 
    int intVal; 
} 

@Test 
public void testRefEqu() { 

    dummy inst1 = new dummy(); 
    inst1.nestedClass = new dummy2(); 
    inst1.nestedClass.intVal = 2; 
    dummy inst2 = new dummy(); 
    inst2.nestedClass = new dummy2(); 
    inst2.nestedClass.intVal = 2; 
    boolean isEqual = EqualsBuilder.reflectionEquals(inst1.nestedClass, inst2.nestedClass); 
    isEqual = EqualsBuilder.reflectionEquals(inst1, inst2); 
} 
+0

Si reflexión es igual se acaba la comparación de referencias, entonces se tiene un error. Debería hacer más que eso. – DwB

+0

@DwB Sospecho que la intención del código es permitirle implementar reflexivamente equals() en una clase específica. Esto es diferente de lo que quiero, que es reflejar en dos instancias de objetos. En este contexto, no es un error, sino una decepción. –

+0

Perdí un medio día de este débil comportamiento indocumentado de EqualsBuilder. Si el campo de un objeto pasado no es primitivo, el builde rjust llama a object.equals(). Muy decepcionante e inútil. – AlexWien

Respuesta

12

De la respuesta a esta pregunta https://stackoverflow.com/a/1449051/116509 y de algunas pruebas preliminares, parece que Unitils' ReflectionAssert.assertReflectionEquals hace lo que usted está esperando . (Editar: pero puede ser abandonado, por lo que podría intentar AssertJ https://joel-costigliola.github.io/assertj/assertj-core-features-highlight.html#field-by-field-recursive)

Estoy muy alarmado por este comportamiento de EqualsBuilder así que gracias por la pregunta. Hay bastantes respuestas en este sitio que lo recomiendan. Me pregunto si la gente que lo recomienda se dio cuenta de que lo hace.

+0

Aunque comparto su sorpresa para ser justo con EqualsBuilder, y al comentar sobre la pregunta, creo que es una cuestión de malentender el propósito de la rutina. Quizás esto es algo que podría hacerse más claro. –

+2

El javadoc debe modificarse en mi humilde opinión para hacerlo más claro. – artbristol

+0

He realizado algunas pruebas iniciales en assertReflectionEquals y parece que funciona. Muchas gracias –

0

Implemente el método equals() en las clases en cuestión. Los iguales de cada llamada compararán la igualdad de las clases anidadas (o, si lo prefieren, compararán la igualdad de los miembros de los datos). Un método equals correctamente escrito siempre dará como resultado una comparación profunda.

En su ejemplo, los dummy iguales clase sería algo como esto:

public boolean equals(Object other) 
{ 
    if (other == this) return true; 
    if (other instanceOf dummy) 
    { 
     dummy dummyOther = (dummy)other; 
     if (nestedClass == dummyOther.nestedClass) 
     { 
      return true; 
     } 
     else if (nestedClass != null) 
     { 
      return nestedClass.equals(dummyOther); 
     } 
     else // nestedClass == null and dummyOther.nestedClass != null. 
     { 
      return false; 
     } 
    } 
    else 
    { 
     return false; 
    } 
} 
+2

Entiendo que esta es la forma normal de lograr esto, y para la mayoría de las situaciones es la forma recomendada. El mecanismo integrado para calcular la igualdad es robusto y extensible para permitir que las clases personalizadas definan qué significa la igualdad para ellos. Lamentablemente, mis requisitos me impiden poder implementar equals() en todas las clases anidadas y, en consecuencia, espero utilizar la reflexión. Gracias –

+0

El problema con este enfoque es que no puede manejar gráficos de objetos cíclicos. La comparación de gráficos de objetos cíclicos usando este enfoque daría como resultado una recursión infinita. – jhegedus

+0

Acepto que los gráficos de objetos cíclicos serán un problema. como lo son con la serialización y la conmutación por error (que probablemente sea solo un síntoma de la serialización). – DwB

3

Un método sería comparar los objetos que utilizan la reflexión - pero esto es complicado. Otra estrategia sería la de comparar las matrices de bytes de los objetos serializados:

class dummy implements Serializable { 
    dummy2 nestedClass; 
} 

class dummy2 implements Serializable { 
    int intVal; 
} 

@Test 
public void testRefEqu() throws IOException { 

    dummy inst1 = new dummy(); 
    inst1.nestedClass = new dummy2(); 
    inst1.nestedClass.intVal = 2; 

    dummy inst2 = new dummy(); 
    inst2.nestedClass = new dummy2(); 
    inst2.nestedClass.intVal = 2; 

    boolean isEqual1 = EqualsBuilder.reflectionEquals(inst1.nestedClass, inst2.nestedClass); 
    boolean isEqual2 = EqualsBuilder.reflectionEquals(inst1, inst2); 

    System.out.println(isEqual1); 
    System.out. println(isEqual2); 

    ByteArrayOutputStream baos1 =new ByteArrayOutputStream(); 
    ObjectOutputStream oos1 = new ObjectOutputStream(baos1); 
    oos1.writeObject(inst1); 
    oos1.close(); 

    ByteArrayOutputStream baos2 =new ByteArrayOutputStream(); 
    ObjectOutputStream oos2 = new ObjectOutputStream(baos2); 
    oos2.writeObject(inst1); 
    oos2.close(); 

    byte[] arr1 = baos1.toByteArray(); 
    byte[] arr2 = baos2.toByteArray(); 

    boolean isEqual3 = Arrays.equals(arr1, arr2); 

    System.out.println(isEqual3); 

} 

Su aplicación serializa y deserializa objetos lo que este enfoque parece ser la solución más rápida (en términos de operaciones de la CPU) para su problema.

+0

Saludos Johnny, había pensado en comparar la forma serializada, que es un enfoque ordenado que funciona en torno a las deficiencias de EqualsBuilder. Dicho esto, no validará por completo la serialización y la deserialización, ya que no confirma que la forma original no serializada sea la misma que la forma deserializada. Saludos –

+0

Hola Howard, ¿podrías dar un ejemplo cuando tal situación podría suceder? Estaba seguro de que un objeto tiene exactamente una forma serializada y una representación de bytes. – whysoserious

+0

Hola Johnny, recuerda que la razón para esto es para probar mi código de buggy. Si mi serializador no serializa un campo en particular, la comparación de las versiones serializadas no detectará un problema. –