Java равно (): отражать или не отражать - PullRequest
9 голосов
/ 24 сентября 2008

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

Продолжая, проблема достигла своей цели, когда я забыл проверить одно из полей на равенство. Поэтому мой метод равенства был неверным. Тогда я подумал использовать отражение:

--code removed because it was too distracting--

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

Плюсы:

  • Если добавляется новое поле, оно автоматически включается
  • Метод гораздо более краткий, чем 30, если утверждения

Минусы:

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

Есть мысли?

Ответы [ 9 ]

13 голосов
/ 24 сентября 2008

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

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

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

9 голосов
/ 06 октября 2008

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

Удалите методы hashCode и equals, которые у вас есть.

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

Выберите Source-> Generate hashcode и равно ...

Готово! Больше не нужно беспокоиться об отражении.

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

5 голосов
/ 24 сентября 2008

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

 public boolean equals(Object obj) {
    return EqualsBuilder.reflectionEquals(this, obj);
 }
2 голосов
/ 24 сентября 2008

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

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

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

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

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

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

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

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

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

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

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

1 голос
/ 06 октября 2008

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

, например

@IgnoreEquals
String fieldThatShouldNotBeCompared;

И затем, конечно, вы проверяете наличие аннотации в общем методе equals.

1 голос
/ 24 сентября 2008

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

  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 голос
/ 24 сентября 2008

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

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

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

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

0 голосов
/ 24 сентября 2008
0 голосов
/ 24 сентября 2008

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

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

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

...