Java equals (): отражать или не отражать

Этот вопрос конкретно связан с переопределением метода equals () для объектов с большим количеством полей. Прежде всего, позвольте мне сказать, что этот большой объект не может быть разбит на несколько компонентов без нарушения принципов объектно-ориентированного программирования, поэтому сообщение мне, что «ни один класс не должен иметь более x полей», не поможет.

Дальше проблема решилась, когда я забыл проверить одно из полей на равенство. Следовательно, мой метод equals был неверным. Тогда я подумал использовать отражение:

--code removed because it was too distracting--

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

Плюсы:

  • Если добавлено новое поле, оно включается автоматически.
  • Этот метод намного короче, чем 30 операторов if.

Минусы:

  • Если добавляется новое поле, оно включается автоматически, иногда это нежелательно
  • Производительность: это должно быть медленнее, я не чувствую необходимости разбирать профилировщик
  • Внесение определенных полей в белый список, которые следует игнорировать при сравнении, немного некрасиво

Какие-нибудь мысли?

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
9
0
5 435
9
Перейти к ответу Данный вопрос помечен как решенный

Ответы 9

Ответ принят как подходящий

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

P.S. Если вы выберете этот путь для equals(), не забудьте сделать что-то подобное для hashCode().

P.P.S. Надеюсь, вы уже рассматривали HashCodeBuilder и EqualsBuilder.

Все поля являются строками или примитивами @ P.S. Уже сделал, но напомнить всем не помешает! @ P.P.S. Я знал, что что-то там должно быть. Я использовал ToStringBuilder, понятия не имею, как я это упустил. Настоящий вопрос - размышлять или не размышлять.

oreoshake 24.09.2008 03:52

Это зависит от того, что вы считаете наиболее вероятным: добавить поле и забыть добавить его в equals () или добавить поле, которое вы не хотите и / или не можете использовать в автоматическом сравнении, хотя использование аннотации может помочь. Вы можете рассмотреть что-то вроде flexjson и сравнить вывод каждого объекта.

Hank Gay 24.09.2008 05:19

Вы всегда можете аннотировать поля, которые вам нужны / не нужны в вашем методе equals, это должно быть простым и простым изменением для него.

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

Кроме того, говоря о хэш-картах, у вас есть та же проблема с методом hashCode.

Наконец, действительно ли вам нужно сравнивать все поля на предмет равенства?

Я на самом деле оговорился, у нас есть метод hashcode / equals, который просто использует идентификатор записи (из базы данных), и поскольку он всегда будет уникальным, метод hashCode / equals просто сравнивает идентификаторы. Но нам нужно проверить, эквивалентны ли объекты, если все поля одинаковы.

oreoshake 24.09.2008 03:56

Если у вас есть доступ к именам полей, почему бы вам не сделать стандартом, что поля, которые вы не хотите включать, всегда начинаются с «local», «nochk» или чего-то в этом роде.

Затем вы заносите в черный список все поля, которые начинаются с этого (тогда код не такой уж уродливый).

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

Я думаю, что добавление соглашений для чего-то вроде этого излишне снижает удобочитаемость.

oreoshake 24.09.2008 03:54

В вашем коде есть несколько ошибок.

  1. Вы не можете предположить, что this и obj относятся к одному классу. Действительно, для obj явно разрешено быть любым другим классом. Вы можете начать с if ( ! obj instanceof myClass ) return false;, но это все еще не правильно, потому что obj может быть подклассом this с дополнительными полями, которые могут иметь значение.
  2. Вы должны поддерживать значения null для obj с помощью простого if ( obj == null ) return false;
  3. Вы не можете рассматривать null и пустую строку как равные. Вместо этого обращайтесь с null специально. Самый простой способ - начать со сравнения Field.get(obj) == Field.get(this). Если они оба равны или оба указывают на один и тот же объект, это быстро. (Примечание: это также оптимизация, которая вам нужна, поскольку это медленная процедура.) Если это не удается, вы можете использовать быстрый if ( Field.get(obj) == null || Field.get(this) == null ) return false; для обработки случаев, когда ровно один из них - null. Наконец-то можно использовать обычный equals().
  4. Вы не используете foundMismatch

Я согласен с Хэнком, что [HashCodeBuilder][1] и [EqualsBuilder][2] - лучший вариант. Его легко поддерживать, не нужно много шаблонного кода, и вы избегаете всех этих проблем.

Вот мысль, если вас беспокоит:

1 / Забыть обновить большую серию операторов if для проверки равенства при добавлении / удалении поля.

2 / Производительность этого в методе equals ().

Попробуйте следующее:

a / Вернитесь к использованию длинной последовательности операторов if в вашем методе equals ().

б / иметь единственную функцию, которая содержит список полей (в массиве String) и которая будет проверять этот список на соответствие действительности (т.е. отраженным полям). Если они не совпадают, генерируется исключение.

c / В конструкторе этого объекта используйте синхронизированный однократный вызов этой функции (аналогично одноэлементному шаблону). Другими словами, если это первый объект, созданный этим классом, вызовите функцию проверки, описанную в пункте (b) выше.

Исключение сразу станет очевидным при запуске программы, если вы не обновили операторы if, чтобы они соответствовали отраженным полям; затем вы исправляете операторы if и обновляете список полей из пункта (b) выше.

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

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

Если вы достаточно постараетесь, вы все равно можете получить ваши if-выражения не в соответствии с вашим списком полей и отраженными полями, но исключение гарантирует, что ваш список полей соответствует отраженным полям, и вы просто убедитесь, что обновили if-выражения и поле список одновременно.

Если вы выберете подход отражения, EqualsBuilder по-прежнему будет вашим другом:

 public boolean equals(Object obj) {
    return EqualsBuilder.reflectionEquals(this, obj);
 }

Вы можете использовать аннотации, чтобы исключить поля из проверки

например

@IgnoreEquals
String fieldThatShouldNotBeCompared;

И затем, конечно, вы проверяете наличие аннотации в вашем универсальном методе equals.

Используйте Eclipse, FFS!

Удалите имеющиеся у вас методы hashCode и equals.

Щелкните файл правой кнопкой мыши.

Выберите Source-> Generate hashcode and equals ...

Сделанный! Больше не беспокойтесь об отражении.

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

Хотя этот метод проще, чем писать вручную, он разделяет основные недостатки как рефлексивных, так и ручных методов. Метод все еще может быть устаревшим, когда добавляются поля, а метод не создается повторно, и поля все еще можно проверять, чего не должно быть, когда кто-то регенерирует метод и забывает удалить проверку.

Samuel Edwin Ward 02.03.2012 01:41

Иногда волшебной пули нет. Но самое простое решение - это, как правило, самый простой способ в таких случаях.

MetroidFan2002 02.03.2012 20:22

Другие вопросы по теме