Какие альтернативные библиотеки для сравнения объектов с подробным отчетом о различиях существуют в Java
Что проверяют
Понимаете ли вы разницу между «равны или нет» и «чем именно отличаются». Следом спрашивают, где это нужно на практике: аудит изменений сущности, тесты со сложными DTO, сравнение ответов API между версиями.
Как отвечать
Сразу разведите задачи. equals, Objects.equals и EqualsBuilder возвращают boolean и отчёта не дают. Для отчёта о различиях есть отдельные инструменты:
- JaVers: сравнивает графы объектов,
javers.compare(oldObj, newObj)возвращаетDiffсо списком изменений:ValueChange,NewObject,ObjectRemoved, изменения коллекций. Умеет хранить историю версий в базе, есть стартеры для Spring Boot. Основной выбор для аудита; DiffBuilderиReflectionDiffBuilderиз Commons Lang: класс реализуетDiffable<T>, методdiff()возвращаетDiffResultс именем поля, левым и правым значением. Лёгкий вариант, если Commons Lang в проекте уже есть;- AssertJ
usingRecursiveComparison(): для тестов. Сравнивает поле за полем и в сообщении об ошибке перечисляет все расхождения с путями к полям. Лишнее исключается черезignoringFields, для типов можно задать свои компараторы; - Guava
Maps.difference(): для словарей возвращаетMapDifference: ключи только слева, только справа и ключи с разными значениями; - уровень JSON: объекты переводятся в
JsonNodeчерез Jackson, а разница строится как JSON Patch (RFC 6902), например библиотекой zjsonpatch. В тестах для этого есть JSONassert. Удобно, когда сравнивают ответы API; - java-diff-utils: построчный diff текста, подходит для строковых представлений и снапшот-тестов.
Где спотыкаются
Называют EqualsBuilder или Objects.equal из Guava решением задачи. Они отвечают только «да» или «нет», про отчёт о различиях там ничего нет.
Сравнивают объекты через строку JSON целиком. Порядок элементов в HashSet и ключей в HashMap не гарантирован, и равные объекты окажутся «разными».
Не думают о структуре графа: циклические ссылки, ленивые прокси Hibernate, списки без идентификаторов. В JaVers сущности различаются по полю с @Id, а для списков выбирается алгоритм сравнения, иначе перестановка элементов выглядит как набор изменений.