2012-03-09 13 views
12

Próbuję sprawdzić poprawność serializacji i dekretyzacji procedur, porównując wynikowy obiekt z oryginalnym obiektem. Procedury mogą szeregować dowolne i głęboko zagnieżdżone klasy, dlatego też potrzebuję procedury porównawczej, która może otrzymać oryginalną i ostatnią instancję i odzwierciedlać każdy typ wartości oraz porównywać wartości i iteracyjnie nurkować w typach referencyjnych w celu porównania wartości.Głębokie odblaskowe porównanie jest równe

Próbowałem Apache Commons Lang EqualsBuilder.reflectionEquals(inst1, inst2) ale to nie wydaje się zrobić bardzo głębokie porównania, to po prostu porównuje typy referencyjne dla równości niż nurkowanie w głąb nich:

Poniższy kod ilustruje mój problem. Pierwsze wywołanie do reflectionEquals zwraca wartość true, ale druga zwraca wartość false.

Czy istnieje procedura biblioteki, którą ktoś mógłby polecić?

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

Jeśli odbicie jest równe porównywaniu odniesień, oznacza to błąd. Powinno to zrobić więcej. – DwB

+0

@DwB Podejrzewam, że intencją kodu jest umożliwienie refleksyjnego zaimplementowania equals() w określonej klasie. Różni się to od tego, co chcę, co ma odzwierciedlać dwie instancje obiektu. W tym kontekście nie jest to błąd, ale raczej rozczarowanie! –

+0

Straciłem pół dnia tego słabego nieudokumentowanego zachowania EqualsBuilder. Jeśli pole przekazanego obiektu jest nie-prymitywne, to build-torelease wywołuje object.equals(). Bardzo rozczarowujące i bezużyteczne. – AlexWien

Odpowiedz

12

Od odpowiedzi na to pytanie https://stackoverflow.com/a/1449051/116509 i od jakiegoś wstępnego badania, wygląda Unitils' ReflectionAssert.assertReflectionEquals robi co czekasz . (Edycja: ale może być porzucony, więc możesz spróbować AssertJ https://joel-costigliola.github.io/assertj/assertj-core-features-highlight.html#field-by-field-recursive)

Jestem bardzo zaniepokojony tym zachowaniem EqualsBuilder, więc dziękuję za pytanie. Na tej stronie jest wiele odpowiedzi, które polecają - zastanawiam się, czy ludzie, którzy ją polecają, zdali sobie sprawę, że to robi?

+0

Dzielę się z tobą niespodzianką, że jestem uczciwy wobec EqualsBuilder, i skomentowałem to pytanie, myślę, że jest to kwestia niezrozumienia intencji rutyny. Być może jest to coś, co można wyjaśnić. –

+2

Jawadoc powinien zostać zmodyfikowany IMHO, aby był jaśniejszy. – artbristol

+0

Zrobiłem kilka wstępnych testów na assertReflectionEquals i wydaje się, że działa. Bardzo dziękuję –

0

Wdrożenie metody equals() na zajęciach w pytaniu. Równe wartości każdego połączenia będą porównywać równość klas zagnieżdżonych (lub, jeśli chcesz, będą porównywać równość elementów danych). Prawidłowo napisana metoda równań zawsze spowoduje głębokie porównanie.

W przykładzie, klasa równi dummy byłoby coś takiego:

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

Rozumiem, że jest to normalny sposób osiągnięcia tego celu, a w większości sytuacji jest to zalecany sposób. Wbudowany mechanizm obliczania równości jest solidny i rozszerzalny, aby umożliwić niestandardowym klasom zdefiniowanie, co oznacza dla nich równość. Niestety, moje wymagania uniemożliwiają mi wdrożenie equals() na wszystkich zagnieżdżonych klasach i mam nadzieję, że użyję refleksji. Dzięki –

+0

Problem z tym podejściem polega na tym, że nie radzi sobie z cyklicznymi wykresami obiektów. Porównanie cyklicznych wykresów obiektów za pomocą tego podejścia spowodowałoby nieskończoną rekurencję. – jhegedus

+0

Zgadzam się, że cykliczne wykresy obiektów będą stanowić problem. tak jak z serializacją i awarią (co jest prawdopodobnie tylko symptomem serializacji). – DwB

3

Jedną z metod jest porównywanie obiektów za pomocą odbicia - ale jest to trudne. Inną strategią byłoby porównać tablice bajt odcinkach obiektów:

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

} 

Aplikacja serializes i deserializes przedmiotów więc takie podejście wydaje się być najszybszym rozwiązaniem (w zakresie operacji CPU) dla swojego problemu.

+0

Pozdrowienia Johnny, myślałem o porównaniu serializowanej formy, która jest zgrabnym podejściem, które działa na niedociągnięcia EqualsBuilder. To powiedziawszy, nie będzie w pełni walidować serializacji i dekrealizacji, ponieważ nie do końca potwierdza, że ​​oryginalna niezdyrywalizowana forma jest taka sama jak deserializowana. Pozdrowienia –

+0

Hej Howard, czy mógłbyś podać przykład, kiedy taka sytuacja może się zdarzyć? Byłem pewien, że jeden obiekt ma dokładnie jedną serializowaną postać i reprezentację bajtową. – whysoserious

+0

Cześć Johnny, pamiętaj, że powodem tego jest przetestowanie mojego błędnego kodu. Jeśli mój serializator nie potrafi serializować określonego pola, porównanie wersji zserializowanych nie wykryje problemu. –

Powiązane problemy