Контракты - это своего рода соглашения программиста с определенным правилом написания кода. В Java имеются как встроенные контракты, такие как методы equals() и hasCode(), так и контракты библиотечных зависимостей, например COFOJA от Google.
Рассмотрим использование контракта equals().
Контракт equals()
необходим в Java для подтверждения или отрицания того факта, что два объекта одного происхождения являются логически равными. То есть, сравнивая два объекта, программисту необходимо понять, эквивалентны ли их значимые поля. Не обязательно все поля должны быть идентичны, так как метод equals()
подразумевает именно логическое равенство. Но иногда нет особой необходимости в использовании этого метода. Как говорится, самый легкий путь избежать проблем, используя тот или иной механизм — не использовать его. Также следует заметить, что однажды нарушив контракт equals
вы теряете контроль над пониманием того, как другие объекты и структуры будут взаимодействовать с вашим объектом. И впоследствии найти причину ошибки будет весьма затруднительно.
Thread
. Для них реализации метода equals
, предоставляемого классом Object
, более чем достаточно. Другой пример — классы перечислений (Enum
).java.util.Random
вообще нет необходимости сравнивать между собой экземпляры класса, определяя, могут ли они вернуть одинаковую последовательность случайных чисел. Просто потому, что природа этого класса даже не подразумевает такое поведение.equals
и поведение этой реализации вас устраивает.Set
, List
, Map
реализация equals
находится в AbstractSet
, AbstractList
и AbstractMap
соответственно.equals
, когда область видимости вашего класса является private
или package-private
и вы уверены, что этот метод никогда не будет вызван.При переопределении метода equals
разработчик должен придерживаться основных правил, определенных в спецификации языка Java.
x
, выражение x.equals(x)
должно возвращать true
.x != null
x
и y
, x.equals(y)
должно возвращать true
только в том случае, когда y.equals(x)
возвращает true
.x
, y
и z
, если x.equals(y)
возвращает true
и y.equals(z)
возвращает true
, x.equals(z)
должно вернуть значение true
.x
и y
повторный вызов x.equals(y)
будет возвращать значение предыдущего вызова этого метода при условии, что поля, используемые для сравнения этих двух объектов, не изменялись между вызовами.x
вызов x.equals(null)
должен возвращать false
.Многие классы, например классы из Java Collections Framework, зависят от реализации метода equals()
, поэтому не стоит им пренебрегать, т.к. нарушение контракта этого метода может привести к нерациональной работе приложения и в таком случае найти причину будет достаточно трудно. Согласно принципу рефлексивности, каждый объект должен быть эквивалентен самому себе. Если этот принцип будет нарушен, при добавлении объекта в коллекцию и при последующем поиске его с помощью метода contains()
мы не сможем найти тот объект, который только что положили в коллекцию. Условие симметричности гласит, что два любых объекта должны быть равны независимо от того, в каком порядке они будут сравниваться. Например, имея класс, содержащий всего одно поле строкового типа, будет неправильно сравнивать в методе equals
данное поле со строкой. Т.к. в случае обратного сравнения метод всегда вернет значение false
.
// Нарушение симметричности public class SomeStringify { private String s; @Override public boolean equals(Object o) { if (this == o) return true; if (o instanceof SomeStringify) { return s.equals(((SomeStringify) o).s); } // нарушение симметричности, классы разного происхождения if (o instanceof String) { return s.equals(o); } return false; } }
/Правильное определение метода equals @Override public boolean equals(Object o) { if (this == o) return true; return o instanceof SomeStringify && ((SomeStringify) o).s.equals(s); }