Подтвердить что ты не робот

Глубокое отражающее сравнение равно

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

Я попробовал 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);
}
4b9b3361

Ответ 1

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

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

Ответ 2

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

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

}

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

Ответ 3

Внедрить метод 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;
    }
}