14

There are two similar questions on SO:

Is there a Java utility to do a deep comparison of two objects?

Deep reflective compare equals

but, it is funny, none of them gives a fully correct answer to the question.

What I and other questions' authors really want, is some method in some library which will just tell whether the given two objects are equal or not:

boolean deepEquals(Object obj1, Object obj2)

i.e. without throwing any exception and so on.

apache's EqualsBuilder is not the solution, because it doesn't deep compare.

Unitils seems also be a bad decision, because its method do not return true or false; it just throws an exception if comparison has failed. Of course, it could be used like this:

Difference difference = ReflectionComparatorFactory.createRefectionComparator(new ReflectionComparatorMode[]{ReflectionComparatorMode.LENIENT_ORDER, ReflectionComparatorMode.IGNORE_DEFAULTS}).getDifference(oldObject, newObject);

but it seems to be very ugly, and unitils library entirely seems to be too heavy for the required compare purposes.

Furthermore, it is quite clear how to create such deepEquals by myself, but it is unlikely that there aren't common used libraries which contain an already implemented method like this. Any ideas?

Andremoniy
  • 34,031
  • 20
  • 135
  • 241
  • Recursive comparison is not trivial. Look at the `EqualsBuilder.reflectionEquals` method - it let's you specify what fields to not compare and how far to reach in class hierarchy tree when inheritance is used. Now if you have a nested object which you want to also compare using reflection, how can you tell what fields to ignore? If you don't care, then it is simple to wrap `EqualsBuilder` implementation to achieve what you need. Otherwise you may need to use annotations or a registry to tell for what class what fields to not compare. – krzychu Mar 20 '17 at 06:47

3 Answers3

4

I don't think the Apache EqualsBuilder is a terrible answer, but for a simple interface that does what you want, you would need to wrap the call a bit. Here's an example.

public static <T> boolean matches(T actual, T expected, String... excludedFields) {
    assert actual != null;
    assert expected != null;
    boolean matches = EqualsBuilder.reflectionEquals(actual, expected,
            false /* testTransients */,
            null /* reflectUpToClass */,
            true /* testRecursive */,
            excludedFields);
    if (!matches) {
        log.warn("Actual result doesn't match.");
        logComparison(actual, expected);
    }
    return matches;
}

public static <T>  void logComparison(T actual, T expected) {
    //Making sure hashcodes don't thrown an exception
    assert actual.hashCode() > Integer.MIN_VALUE;
    assert expected.hashCode() > Integer.MIN_VALUE;
    log.warn("Expected: \n" + reflectionToString(expected));
    log.warn("Actual: \n" + reflectionToString(actual));
}
Snekse
  • 15,474
  • 10
  • 62
  • 77
3

Java 1.7 has Objects.deepEquals(Object a, Object b)

Unfortunately Unitils's ReflectionAssert class does not have an accompanying method that returns true or false. You can try:

https://github.com/jdereg/java-util

whose DeepEquals class has DeepEquals.deepEquals(Object a, Object b) and DeepEquals.deepHashCode(Object o) methods which you can use to override your class's equals and hashCode methods or use in an external Comparator or Equivalence.

Note: This DeepEquals implementation has a limitation. If the class has a user defined overridden equals methods, that will take precedence.

rizjoj
  • 197
  • 2
  • 9
  • 4
    The author of the question did not write it explicitly, but I think based on referenced questions, that he wants a reflection equals, that is comparing objects, without implementing `equals` method on them. The `Objects.deepEquals(Object a, Object b)` does not compare using reflection. It relies on `equals` method and `deep` part refers to comparing arrays, not fields with reference types. – krzychu Mar 20 '17 at 06:39
0

If you're looking into testing, AssertJ offers a recursive comparison function:

assertThat(new).usingRecursiveComparison().isEqualTo(old);

See AssertJ's documentation for details: https://assertj.github.io/doc/#basic-usage

Prerequisites for using AssertJ:

import:

import static org.assertj.core.api.Assertions.*;

maven dependency:

    <!-- test -->
    <dependency>
        <groupId>org.assertj</groupId>
        <artifactId>assertj-core</artifactId>
        <version>3.19.0</version>
        <scope>test</scope>
    </dependency>
fl0w
  • 3,593
  • 30
  • 34