На какое равенство проверяет метод Apache Commons ObjectUtils equals?

Я всегда понимал, что в Java есть два типа равенства:

  • равенство значений: использует метод .equals() для проверки того, что два объекта реализуют отношение эквивалентности для ненулевых ссылок на объекты.
  • ссылочное равенство: использует оператор == для проверки равенства двух примитивных типов или ячеек памяти.

На следующих страницах более подробно описаны основы этого языка.

То, что ни одна из этих ссылок явно не указывает, - это то, что должно произойти, если две null ссылки на объекты сравниваются на равенство значений. Неявное предположение состоит в том, что должен быть выдан NullPointerException, но это не то, что делает ObjectUtils.equals (), который можно рассматривать как практический служебный метод. .

Что меня беспокоит, так это то, что Apache Commons, похоже, эффективно ввел третью меру равенства в Java с черного хода и что и без того запутанное положение дел могло быть значительно более сложным. Я называю это третьей мерой равенства, потому что она пытается проверить равенство значений, а когда это не удается, возвращается к проверке на равенство ссылок. Тест на равенство Apache Commons имеет много общего с равенством значений и ссылочным равенством, но также заметно отличается.

Прав ли я, что беспокоюсь и хочу избегать использования ObjectUtils.equals() там, где это возможно?

Есть ли аргумент в пользу утверждения, что ObjectUtils.equals() обеспечивает полезное объединение двух других мер равенства?

Выбранный ответ

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


person Caoilte    schedule 25.01.2010    source источник
comment
+1 за хорошо проработанный вопрос   -  person trashgod    schedule 25.01.2010
comment
Вы можете объяснить, почему это третья мера равенства? Я думаю, что сравнение двух нулевых ячеек памяти (sic) и получение истины ничем не отличается от сравнения двух примитивных значений 0 и получения истины. NullPointerException предназначен для попытки разыменовать нулевой указатель ... полностью отличается от простой проверки его значения.   -  person PSpeed    schedule 25.01.2010
comment
Я обновил вопрос, чтобы явно указать, чем тест равенства Apache Commons отличается от двух методов проверки равенства Java, но проблема, которую игнорирует ваше предположение, более сложна. Когда я проверяю равенство значений, я не сравниваю две ячейки памяти с нулевым значением. JLS заявляет об этом совершенно четко. То, что ячейки памяти иногда сравниваются, так или иначе, это деталь / ярлык реализации. Он не указан, и в случае, когда оба объекта равны нулю, это неприемлемо. Null не является объектом в java.   -  person Caoilte    schedule 28.01.2010
comment
Но неясно, почему сравнение двух вещей, которых нет, и объект не должны быть равными. В этом случае кажется логичным вернуть истину ... особенно когда наиболее вероятным сравнением являются значения полей двух объектов. Объект A .equals () Объект B, если A.foo == B.foo, даже если foo указывает не на объект.   -  person PSpeed    schedule 28.01.2010


Ответы (3)


Вот код ObjectUtils.equals(..):

public static boolean equals(Object object1, Object object2) {
     if (object1 == object2) {
       return true;
     }
     if ((object1 == null) || (object2 == null)) {
       return false;
    }
    return object1.equals(object2);
}

документы ObjecUtils четко заявляют, что переданные объекты могут иметь значение NULL.

Теперь о том, нужно ли возвращать true, если вы сравниваете два null. На мой взгляд - нет, потому что:

  • когда вы сравниваете два объекта, вы, вероятно, собираетесь что-то с ними сделать позже. Это приведет к NullPointerException
  • передача двух null для сравнения означает, что они откуда-то взялись, а не «настоящие» объекты, возможно, из-за какой-то проблемы. В этом случае сравнивать их по отдельности неправильно - выполнение программы должно было быть остановлено до этого.
  • В пользовательской библиотеке, которую мы здесь используем, у нас есть метод equalOrBothNull(), который отличается от метода equals в этой утилите сравнением NULL.
person Bozho    schedule 25.01.2010
comment
Если оба объекта равны нулю, возвращается истина. - person Michael Borgwardt; 25.01.2010
comment
Это неверно. System.out.println("Is null == null? " + (null == null)); распечатывает true - person matt b; 25.01.2010
comment
Я согласен с тем, что в документации ObjecUtils довольно четко указано, что они делают. Меня больше беспокоило то, что они не предупреждают вас о том, что их определение равенства отличается от двух в JLS. Меня также больше всего беспокоило то, что такая слабая проверка на равенство скрывает основные ошибки. - person Caoilte; 25.01.2010
comment
Но это не отличается от JLS, не так ли? Строка foo = null; Целочисленная полоса = ноль; System.out.println (foo == bar); Вы должны увидеть правду. - person PSpeed; 25.01.2010
comment
Это другое, потому что это не ObjectUtils. == это ObjectUtils.equals (), который подразумевает проверку равенства значений, которая должна вызывать исключение NullPointerException. Даже если бы это был ObjectUtils. ==, это все равно вводило бы в заблуждение, потому что проверка на равенство ссылок не должна вызывать метод .equals () параметров объекта. - person Caoilte; 28.01.2010
comment
Если A.equals (B) ложно, а A == B истинно, тогда возникают большие большие проблемы. Но проверка A == B может быть в тысячи раз быстрее в некоторых случаях, когда ссылки на самом деле одинаковы. И я не могу придумать ни одного варианта использования, который требовал бы от вызывающего сначала выполнить нулевую проверку ... и все же каждый вариант использования, в котором мне когда-либо требовался такой метод утилиты, требовал возможности передавать нули. - person PSpeed; 28.01.2010
comment
@Bozho, можете ли вы добавить ссылку на Springs [nullSafeEquals] (static.springsource.org/spring/docs/3.0.x/api/org/, java.lang.Object% 29 - person Adam Gent; 21.08.2012
comment
Вопрос касается apache commons, но я поддержал ваш комментарий, чтобы людям было легче его увидеть. - person Bozho; 22.08.2012
comment
@Bozho, а можно вместо Equalsbuilder использовать? - person kaushik; 15.08.2016

Правильно ли я обеспокоен и хочу избегать использования ObjectUtils.equals () везде, где это возможно?

Нет. То, что вам нужно считать равным, зависит от ваших требований. И желание считать два значения NULL равными, а любое ненулевое значение, не равным нулю, без необходимости иметь дело с NullPointerExceptions, является очень, очень распространенным требованием (например, когда вы хотите запускать события изменения значения из установщика).

Собственно, это то, как equals() в целом должно работать, и обычно половина этого поведения реализуется (в документе API Object.equals() говорится: «Для любого ненулевого ссылочного значения x x.equals(null) должно возвращать false»). - что это не работает наоборот, в основном из-за технических ограничений (язык был разработан без множественных отправка для упрощения).

person Michael Borgwardt    schedule 25.01.2010

Если вас это беспокоит, вы можете 1) не использовать этот метод 2) написать свой собственный, чтобы обернуть его

public class MyObjectUtils {
    public static boolean equals(Object obj1, Object obj2) {
        return obj1 != null && obj2 != null && ObjectUtils.equals(obj1, obj2);
    }
}

Мне кажется странным допускать, чтобы значение null было равно нулю, но это не кажется большой проблемой. По большей части я бы не ожидал, что мое приложение даже попадет в пути кода, которые включают тесты на равенство, если один или несколько объектов имеют значение NULL.

person matt b    schedule 25.01.2010
comment
ну (как я сказал в своем изначально неправильном заявлении) (null == null) = true может быть хорошим источником для NPE в дальнейшем. - person Bozho; 25.01.2010
comment
Позвольте мне перефразировать, мне кажется странным когда-либо проверять две ссылки, которые обе являются нулевыми на равенство - person matt b; 25.01.2010
comment
Давайте посмотрим ... места, где я сравнивал ноль с нулем: почти каждый метод .equals (), который я когда-либо писал для объектов с непримитивными полями, каждая отправка PropertyChangeEvent, которую я когда-либо писал, ... человек, это просто так часто возникает. - person PSpeed; 25.01.2010
comment
Конечно, есть легкий обходной путь. Меня беспокоило то, что Apache Commons как лучшая библиотека утилит может влиять на разработчиков и что в этом случае результат может отрицательно сказаться на качестве последующего кода. Я работаю над множеством различных баз кода для множества разных клиентов, и многие используют Apache Commons везде, где могут, не задумываясь, стоит ли им это делать. - person Caoilte; 28.01.2010
comment
Код утилиты предназначен для уменьшения количества операций вырезания и вставки, которые необходимо выполнять. В этом случае для 100% случаев использования такого служебного метода у меня потребовалось бы добавить дополнительную проверку. На самом деле, поверх приведенного выше примера метода мне также пришлось бы добавить obj1 == obj2 (который охватывает мой другой случай), потому что иногда это в 1000 раз быстрее, когда ссылки одинаковы. - person PSpeed; 28.01.2010