2012-03-09 10 views
12

Sto provando a convalidare le routine serialize e de-serialize confrontando l'oggetto risultante con l'oggetto originale. Le routine possono serializzare classi arbitrarie e profondamente annidate e di conseguenza voglio una routine di confronto che possa essere data all'istanza originale e finale e riflettere in modo riflessivo ogni tipo di valore e confrontare i valori e immergersi iterativamente in tipi di riferimento per confrontare i valori.Confronto Deep Reflective uguale a

Ho provato l'Apache Commons Lang EqualsBuilder.reflectionEquals(inst1, inst2) ma questo non sembra fare un confronto molto profondo, si confronta semplicemente i tipi di riferimento per l'uguaglianza, piuttosto che le immersioni più in profondità nel loro:

Il codice seguente illustra il mio problema. La prima chiamata a reflectionEquals restituisce true ma la seconda restituisce false.

Esiste una routine di libreria che chiunque potrebbe consigliare?

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

Se la riflessione equivale al confronto dei riferimenti, ha un bug. Dovrebbe fare di più. – DwB

+0

@DwB Sospetto che l'intento del codice sia quello di consentire di implementare in modo riflessivo equals() in una classe specifica. Questo è diverso da ciò che voglio, che è quello di riflettere su due istanze di oggetti. In questo contesto non è un bug ma piuttosto una delusione! –

+0

Ho perso una mezza giornata, di questo debole comportamento non documentato di EqualsBuilder. Se il campo di un oggetto passato è un non primitivo, la builde rjust chiama object.equals(). Molto deludente e inutile. – AlexWien

risposta

12

Dalla risposta a questa domanda https://stackoverflow.com/a/1449051/116509 e da alcuni test preliminari, sembra che Unitils' ReflectionAssert.assertReflectionEquals fa quello che ti aspetti . (Modifica: ma potrebbe essere abbandonato, quindi potresti provare AssertJ https://joel-costigliola.github.io/assertj/assertj-core-features-highlight.html#field-by-field-recursive)

Sono molto allarmato da questo comportamento di EqualsBuilder quindi grazie per la domanda. Ci sono molte risposte su questo sito che lo raccomandano - mi chiedo se le persone che lo raccomandano si siano rese conto di farlo?

+0

Condivido la tua sorpresa per essere onesti con EqualsBuilder, e mentre ho commentato la domanda, penso che si tratti di fraintendere l'intento della routine. Forse questo è qualcosa che potrebbe essere reso più chiaro. –

+2

Il javadoc dovrebbe essere modificato IMHO per renderlo più chiaro. – artbristol

+0

Ho effettuato alcuni test iniziali su assertReflectionEquals e sembra funzionare. Grazie mille –

0

Implementare il metodo equals() nelle classi in questione. Gli uguali di ogni chiamata confronteranno l'uguaglianza delle classi nidificate (o, se lo desideri, confronteranno l'uguaglianza dei membri dei dati). Un metodo uguaglianza scritto correttamente porterà sempre a un confronto profondo.

Nel tuo esempio, di uguale classe dummy sarebbe qualcosa di simile:

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

Capisco che questo è il modo normale per ottenere questo, e per la maggior parte delle situazioni è il modo consigliato. Il meccanismo integrato per il calcolo dell'eguaglianza è robusto ed estensibile per consentire alle classi personalizzate di definire cosa significhi per loro l'uguaglianza. Sfortunatamente le mie esigenze mi impediscono di implementare equals() su tutte le classi nidificate e di conseguenza spero di usare la reflection. Grazie –

+0

Il problema con questo approccio è che non può gestire grafici di oggetti ciclici. Confrontando i grafici di oggetti ciclici usando questo approccio si otterrebbe una ricorsione infinita. – jhegedus

+0

Concordo sul fatto che i grafici degli oggetti ciclici rappresenteranno un problema. come sono con serializzazione e failover (che è probabilmente solo un sintomo di serializzazione). – DwB

3

Un metodo sarebbe quello di confrontare gli oggetti usando il riflesso - ma questo è difficile. Un'altra strategia potrebbe essere quella di confrontare array di byte di oggetti serializzati:

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); 

} 

L'applicazione serializza e deserializza oggetti per cui questo approccio sembra essere la soluzione più veloce (in termini di operazioni di CPU) per il vostro problema.

+0

Saluti Johnny, avevo pensato di confrontare il modulo serializzato che è un approccio pulito che funziona attorno alle carenze di EqualsBuilder. Detto questo, non convalida completamente la serializzazione e la de-serializzazione in quanto non conferma affatto che la forma non serializzata originale sia la stessa della forma deserializzata. Cordiali saluti, –

+0

Hey Howard, potresti fare un esempio quando una tale situazione potrebbe accadere? Ero sicuro che un oggetto ha esattamente una rappresentazione serializzata di forma e byte. – whysoserious

+0

Ciao Johnny, ricorda che la ragione di questo è di testare il mio codice bacato. Se il mio serializzatore non riesce a serializzare un campo particolare, il confronto tra le versioni serializzate non rileva un problema. –