Я пытаюсь написать модульные тесты для различных clone()
операций внутри большого проекта, и мне интересно, есть ли где-нибудь существующий класс, способный взять два объекта одного типа, провести глубокое сравнение и сказать, если они идентичны или нет?
java
comparison
equals
Ури
источник
источник
Ответы:
Unitils имеет такую функциональность:
источник
unitils
ошибочен именно потому, что он сравнивает переменные, даже если они могут не иметь заметного влияния . Еще одно (нежелательное) последствие сравнения переменных заключается в том, что чистые замыкания (без собственного состояния) не поддерживаются. Кроме того, он требует, чтобы сравниваемые объекты относились к одному и тому же типу среды выполнения. Я закатал рукава и создал свою собственную версию инструмента глубокого сравнения, которая решает эти проблемы.Мне нравится этот вопрос! В основном потому, что на него почти никогда не отвечают или плохо отвечают. Вроде еще никто не понял. Девственная территория :)
Во-первых, даже не думайте об использовании
equals
. Контрактequals
, как определено в javadoc, является отношением эквивалентности (рефлексивным, симметричным и транзитивным), а не отношением равенства. Для этого он также должен быть антисимметричным. Единственная реализацияequals
этого отношения (или когда-либо могло бы быть) истинное отношение равенства - это отношение вjava.lang.Object
. Даже если вы все-таки использовалиequals
для сравнения все на графике, риск разрыва контракта довольно высок. Как указал Джош Блох в Эффективной Java , контракт равных очень легко нарушить:Кроме того, какая польза от логического метода в любом случае? Было бы неплохо на самом деле инкапсулировать все различия между оригиналом и клоном, не так ли? Кроме того, я предполагаю, что вы не хотите беспокоиться о написании / поддержке кода сравнения для каждого объекта на графике, а скорее ищете что-то, что будет масштабироваться вместе с исходным кодом, поскольку он изменяется с течением времени.
То, что вам действительно нужно, это какой-то инструмент для сравнения состояний. То, как этот инструмент реализован, действительно зависит от природы вашей модели предметной области и ваших ограничений производительности. По моему опыту, универсальной волшебной пули не существует. И это будет медленным на большом количестве итераций. Но для проверки полноты операции клонирования он отлично справится со своей задачей. Два ваших лучших варианта - это сериализация и отражение.
Некоторые проблемы, с которыми вы столкнетесь:
XStream работает довольно быстро и в сочетании с XMLUnit выполнит работу всего за несколько строк кода. XMLUnit хорош тем, что может сообщать обо всех различиях или просто останавливаться на первом найденном. И его вывод включает xpath к разным узлам, что приятно. По умолчанию он не разрешает неупорядоченные коллекции, но его можно настроить для этого. Внедрение специального обработчика различий (называемого a
DifferenceListener
) позволяет указать способ обработки различий, включая игнорирование порядка. Однако, как только вы захотите сделать что-либо, кроме простейшей настройки, становится трудно писать, и детали, как правило, привязаны к определенному объекту предметной области.Лично я предпочитаю использовать отражение для циклического перебора всех объявленных полей и детализации каждого из них, отслеживая различия по мере продвижения. Предупреждение: не используйте рекурсию, если вам не нравятся исключения переполнения стека. Держите вещи в области видимости с помощью стека (используйте
LinkedList
или что-то). Я обычно игнорирую временные и статические поля, и я пропускаю пары объектов, которые я уже сравнивал, поэтому я не попадаю в бесконечные циклы, если кто-то решил написать самореферентный код (однако я всегда сравниваю примитивные оболочки, независимо от того, что , поскольку одни и те же ссылки на объекты часто используются повторно). Вы можете настроить все заранее, чтобы игнорировать порядок сбора и игнорировать специальные типы или поля, но мне нравится определять свои политики сравнения состояний для самих полей с помощью аннотаций. Это, IMHO, именно для этого и предназначались аннотации, чтобы сделать метаданные о классе доступными во время выполнения. Что-то вроде:Я думаю, что это действительно сложная проблема, но полностью решаемая! И если у вас есть что-то, что работает для вас, это действительно очень удобно :)
Удачи. И если вы придумаете что-то гениальное, не забудьте поделиться!
источник
См. DeepEquals и DeepHashCode () в java-util: https://github.com/jdereg/java-util
Этот класс делает именно то, что требует оригинальный автор.
источник
public
должно применяться ко всем типам, в отличие от применимости только к коллекции / картам.Переопределить метод equals ()
Вы можете просто переопределить метод equals () класса с помощью EqualsBuilder.reflectionEquals (), как описано здесь :
источник
Просто нужно было реализовать сравнение двух экземпляров сущностей, переработанных Hibernate Envers. Я начал писать свой собственный текст, но потом нашел следующую структуру.
https://github.com/SQiShER/java-object-diff
Вы можете сравнить два объекта одного типа, и он покажет изменения, добавления и удаления. Если изменений нет, то объекты равны (теоретически). Приведены аннотации для геттеров, которые следует игнорировать во время проверки. Фреймворк имеет гораздо более широкое применение, чем проверка равенства, т. Е. Я использую для создания журнала изменений.
Его производительность в порядке, при сравнении сущностей JPA обязательно сначала отсоедините их от диспетчера сущностей.
источник
Я использую XStream:
источник
В AssertJ вы можете:
Вероятно, это не будет работать во всех случаях, однако это будет работать в большем количестве случаев, чем вы думаете.
Вот что говорится в документации:
источник
isEqualToComparingFieldByFieldRecursively
теперь устарела. ИспользуйтеassertThat(expectedObject).usingRecursiveComparison().isEqualTo(actualObject);
вместо этого :)http://www.unitils.org/tutorial-reflectionassert.html
источник
Hamcrest имеет Matcher samePropertyValuesAs . Но он полагается на конвенцию JavaBeans (использует геттеры и сеттеры). Если сравниваемые объекты не имеют геттеров и сеттеров для своих атрибутов, это не сработает.
Пользовательский компонент - с геттерами и сеттерами
источник
isFoo
метод чтения дляBoolean
свойства. Есть PR, который открыт с 2016 года, чтобы исправить это. github.com/hamcrest/JavaHamcrest/pull/136Если ваши объекты реализуют Serializable, вы можете использовать это:
источник
Ваш пример связанного списка не так уж и сложен. По мере того, как код проходит по двум графам объектов, он помещает посещенные объекты в набор или карту. Перед переходом к другой ссылке на объект этот набор проверяется, чтобы увидеть, был ли уже пройден объект. Если да, то дальше идти не надо.
Я согласен с указанным выше человеком, который сказал использовать LinkedList (например, стек, но без синхронизированных методов, поэтому он работает быстрее). Идеальным решением является обход графа объекта с использованием стека и одновременное использование отражения для получения каждого поля. Написанные однажды, этот «внешний» equals () и «внешний» hashCode () - это то, что должны вызывать все методы equals () и hashCode (). Никогда больше вам не понадобится метод клиента equals ().
Я написал небольшой код, который проходит через полный граф объектов, перечисленных в Google Code. См. Json-io (http://code.google.com/p/json-io/). Он сериализует граф объектов Java в JSON и десериализует его. Он обрабатывает все объекты Java, с общедоступными конструкторами или без них, Serializeable или не Serializable и т. Д. Этот же код обхода будет основой для внешней реализации equals () и внешней реализации hashcode (). Кстати, JsonReader / JsonWriter (json-io) обычно быстрее, чем встроенный ObjectInputStream / ObjectOutputStream.
Этот JsonReader / JsonWriter можно использовать для сравнения, но с хэш-кодом это не поможет. Если вам нужен универсальный hashcode () и equals (), ему нужен собственный код. Возможно, я смогу осуществить это с помощью обычного посетителя графика. Посмотрим.
Другие соображения - статические поля - это просто - их можно пропустить, потому что все экземпляры equals () будут иметь одинаковое значение для статических полей, поскольку статические поля являются общими для всех экземпляров.
Что касается временных полей - это будет вариант выбора. Иногда вы можете захотеть, чтобы переходные процессы учитывались, а иногда нет. «Иногда ты чувствуешь себя психом, иногда нет».
Вернитесь к проекту json-io (для других моих проектов), и вы найдете внешний проект equals () / hashcode (). У меня пока нет названия для этого, но это будет очевидно.
источник
Apache дает вам что-то, преобразовать оба объекта в строку и сравнить строки, но вам нужно переопределить toString ()
Переопределить toString ()
Если все поля являются примитивными типами:
Если у вас есть непримитивные поля и / или коллекция и / или карта:
источник
Думаю, вы это знаете, но теоретически вы должны всегда переопределять .equals, чтобы утверждать, что два объекта действительно равны. Это означало бы, что они проверяют переопределенные методы .equals на своих членах.
Именно поэтому .equals определен в Object.
Если бы это делалось последовательно, у вас не было бы проблем.
источник
Гарантия остановки для такого глубокого сравнения может быть проблемой. Что должно делать следующее? (Если вы реализуете такой компаратор, это станет хорошим модульным тестом.)
Вот еще один:
источник
Using an answer instead of a comment to get a longer limit and better formatting.
Если это комментарий, то зачем использовать раздел ответов? Вот почему я отметил это. не из-за?
. Этот ответ уже отмечен кем-то другим, который не оставил комментарий. Я только что получил это в очереди на рассмотрение. Могло быть мое бедствие в том, что я должен был быть более осторожным.Я думаю, что самое простое решение, вдохновленное решением Рэя Хулхи, - это сериализовать объект, а затем глубоко сравнить необработанный результат.
Сериализация может быть байтовой, json, xml или простой toString и т. Д. ToString кажется дешевле. Lombok генерирует для нас бесплатную легко настраиваемую строку ToSTring. См. Пример ниже.
источник
Так как Java 7
Objects.deepEquals(Object, Object)
.источник