Страницы

Поиск по вопросам

четверг, 28 ноября 2019 г.

Метод equals() в Java

#java


Как правильно переопределить метод equals() в Java?
    


Ответы

Ответ 1



Основные принципы при проектировании метода equals: Рефлексивность: для любых не-null объектов x, x.equals(x) должен возвращать true. Симметричность: для любых не-null объектов x и y, x.equals(y) должен возвращать true, если и только если y.equals(x) возвращает true. Транзитивность: для любых не-null объектов x, y, и z, если x.equals(y) возвращает true и y.equals(z) возвращает true, тогда x.equals(z) должен возвращать true. Постоянство: повторный вызов метода equals() должен возвращать одно и тоже значение до тех пор, пока какое-либо значение свойств объекта не будет изменено. То есть, если два объекта равны, то они будут равны пока их свойства остаются неизменными. Для любых не-null объектов x, x.equals(null) должно возвращать false. Также стоит обратить внимание на то, что аргументом метода equals всегда является объект класса Object. Например, переопределяя метод для класса Person, мы должны использовать следующую сигнатуру: public boolean equals(Object person). Зачастую начинающими допускается ошибка и используется сигнатура public boolean equals(Person person), что приводит к перегрузке метода equals. Метод equals взаимосвязан с методом hashCode: для одинаковых объектов значение, возвращаемое методом hashCode, должно быть одинаково. Поэтому зачастую переопределяются оба метода.

Ответ 2



Приведу пример, по которому все будет понятно, также не надо забывать, что при переопределении метода equals() необходимо переопределить метод hashCode(). Если метод equals() возвращает истину для двух объектов, то их хэш-код должен быть одинаковым. Обратное утверждение не верно. TestClass { private int id; public boolean equals(Object other) { if(!super.equals(other)) return false; if (this == other) return true; if (other == null) return false; if(this.getClass() != other.getClass()) return false; TestClass otherObj = (TestClass)other; return this.id == otherObj.id; } public int hashCode() { return 76+133*id; } }

Ответ 3



Прошу пояснить код. В частности, вот эту строку if (!super.equals(other)) return false; Основной вопрос состоит в следующем. Как Вы думаете, что делает эта строчка процитированного кода? Да, там вызывается метод equals родительского класса. А кто у нас у класса в данном случае родитель? Если верить спецификации, то это java.lang.Object. Значит сначала мы в первой строке Вашего кода вызываем метод equals класса Object. Как Вы думаете, что он делает? Вроде как это известная информация, но для большей достоверности вот цитата из исходников public boolean equals(Object obj) { return (this == obj); } То есть он возвращает true только в том случае, когда у нас ссылки на объект совпадают. Если же у нас 2 отдельных объекта, которые хранятся в памяти в разных местах (пусть у них содержимое одинаковое), ссылки на них не совпадают! А что это значит? Это значит, что благодаря строке if(!super.equals(other)) return false; Ваш метод equals будет возвращать true, только если мы будем сравнивать объект сам с собой! Наверное, это не то, чего бы всем хотелось, не так ли? Думаю, эту строку надо убрать и тогда это будет больше похоже на правду. Но вообще каждый раз при написании своего equals надо принимать во внимание тот факт, что когда-то создатели Java и класса java.lang.Object в частности написали условия, которым должен удовлетворять переопределённый метод equals, найти их можно в исходниках, либо здесь. И ещё заметка относительно hashCode. Вы упомянули только одно из требований котракта по hashCode: если 2 объекта считаются равными (equals возвращает true), то hashCode для них должен возвращать одинаковое значение. Дальше Обратное утверждение не верно. на самом деле в контракте, найти можно - здесь стоит продолжение: "However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hashtables". То есть надо принимать этот факт во внимание, как и ещё один пункт из этого же контракта.

Ответ 4



Вариант использования equals при наследовании. Пусть есть класс точка public class Point { private int x; private int y; public Point(int x, int y) { this.x = x; this.y = y; } public boolean equals(Object obj) { if (obj == null) return false; // проверка на случай, если сравнение с самим собой if (obj == this) return true; if (obj.getClass() == this.getClass()) { Point point = (Point) obj; if (point.x == this.x && point.y == this.y) return true; } return false; } } Теперь пришла в голову мысль написать расширение Point3D и для него также переопределить метод equals public class Point3D extends Point { private int z; public Point3D(int x, int y, int z) { super(x, y); this.z = z; } public boolean equals(Object obj) { if (super.equals(obj)) { /* * строки не нужны: эти проверки выполняется в базовом классе * if (this == other) return true; * if (other == null) return false; * if(this.getClass() != other.getClass()) return false; */ Point3D p3d = (Point3D) obj; if (p3d.z == this.z) return true; } return false; } }

Ответ 5



Имеем простой класс такой структуры: class A { final B someNonNullField; C someOtherField; int someNonStateField; } Такой метод определения метода equals будет подойдет во многих ситуациях: public boolean equals(Object other) { // Не строго необходимо, но обычно неплохо для оптимизации if (this == other) return true; if (!(other instanceof A)) return false; A otherA = (A) other; return (someNonNullField.equals(otherA.someNonNullField)) && ((someOtherField == null) ? otherA.someOtherField == null : someOtherField.equals(otherA.someOtherField))); } Действительно нельзя забывать о методе hashCode. сойдет такая его реализация: public int hashCode() { int hash = 1; hash = hash * 31 + someNonNullField.hashCode(); hash = hash * 31 + (someOtherField == null ? 0 : someOtherField.hashCode()); return hash; } Более подробно написано с этой статье от ibm deweloperworks.

Комментариев нет:

Отправить комментарий