2012-03-09 3 views
12

Я пытаюсь проверить процедуры сериализации и де-сериализации, сравнивая результирующий объект с исходным объектом. Подпрограммы могут сериализовать произвольные и глубоко вложенные классы, и, следовательно, мне нужна процедура сравнения, которая может быть предоставлена ​​исходному и окончательному экземпляру и рефлексивно пройти через каждый тип значения и сравнить значения и итеративно погрузиться в ссылочные типы для сравнения значений.Глубокое отражающее сравнение равно

Я попытался Apache Commons Lang EqualsBuilder.reflectionEquals(inst1, inst2), но это не кажется, делают очень глубокое сравнение, оно просто сравнивает ссылочные типы равенства, а не погружаться глубже и глубже в них:

Следующий код иллюстрирует мой вопрос. Первый вызов reflectionEquals возвращает true, а второй возвращает false.

Есть ли библиотека, которую каждый может порекомендовать?

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

Если отражение равно просто сравнению ссылок, то у него есть ошибка. Он должен сделать больше, чем это. – DwB

+0

@ DwB Я подозреваю, что цель кода - позволить вам рефлексивно реализовать equals() в определенном классе. Это отличается от того, что я хочу, чтобы отразить два экземпляра объекта. В этом контексте это не ошибка, а скорее разочарование! –

+0

Я потерял половину дня, этого слабого недокументированного поведения EqualsBuilder. Если поле переданного объекта является не примитивным, то builde rjust вызывает object.equals(). Очень разочаровывает и бесполезно. – AlexWien

ответ

12

Из ответа на этот вопрос https://stackoverflow.com/a/1449051/116509 и от некоторого предварительного тестирования, это выглядит как Unitils' ReflectionAssert.assertReflectionEquals делает то, что вы ожидаете , (Изменить: но может быть отказано, поэтому вы можете попробовать AssertJ https://joel-costigliola.github.io/assertj/assertj-core-features-highlight.html#field-by-field-recursive)

Я очень встревожен этим поведением EqualsBuilder, поэтому спасибо за вопрос. На этом сайте есть немало ответов, рекомендующих это - интересно, рекомендуют ли люди это понять, что это делает?

+0

Я разделяю ваше удивление, хотя, честно говоря, EqualsBuilder, и, когда я прокомментировал этот вопрос, я думаю, что это вопрос непонимания намерения рутины. Возможно, это то, что можно было бы сделать более ясным. –

+2

Javadoc следует изменить IMHO, чтобы сделать его более ясным. – artbristol

+0

Я провел начальное тестирование assertReflectionEquals и, похоже, работает. Большое спасибо –

0

Внесите метод equals() на соответствующие классы. Соотношения каждого вызова будут сравнивать равенство вложенных классов (или, если хотите, сравнивают равенство членов данных). Правильно написанный метод равен всегда будет приводить к глубокому сравнению.

В вашем примере dummy класса равно будет что-то вроде этого:

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

Я понимаю, что это нормальный способ достижения этого, и для большинства ситуаций это рекомендуется. Встроенный механизм вычисления равенства является надежным и расширяемым, чтобы позволить пользовательским классам определять, что означает для них равенство. К сожалению, мои требования не позволяют мне реализовать equals() для всех вложенных классов и, следовательно, я надеюсь использовать рефлексию. Спасибо –

+0

Проблема с этим подходом заключается в том, что он не может обрабатывать графики циклических объектов. Сравнение графиков циклических объектов с использованием этого подхода приведет к бесконечной рекурсии. – jhegedus

+0

Я согласен, что циклические графы объектов будут проблемой. поскольку они с сериализацией и сбой (что, вероятно, является лишь симптомом сериализации). – DwB

3

Один из методов заключается в сравнении объектов с использованием отражения - но это сложно. Другая стратегия будет сравнить массивы байт из сериализованных объектов:

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

} 

Вашего приложение упорядочивает и десериализует объекты так, этот подход, как представляется, самое быстрым решением (в терминах операций ЦП) для вашей проблемы.

+0

Привет, Джонни, я подумал о сравнении сериализованной формы, которая является аккуратным подходом, который работает с недостатками EqualsBuilder. Тем не менее, он не будет полностью проверять сериализацию и де-сериализацию, поскольку он не совсем подтверждает, что оригинальная неэтериализованная форма такая же, как десериализованная форма. С уважением –

+0

Эй, Ховард, не могли бы вы привести пример, когда такая ситуация может случиться? Я был уверен, что один объект имеет ровно одну сериализованную форму и представление байтов. – whysoserious

+0

Привет, Джонни, помните, что причина этого заключается в том, чтобы проверить мой багги-код. Если мой сериализатор не сможет сериализовать конкретное поле, тогда сравнение сериализованных версий не обнаружит проблемы. –

Смежные вопросы