top of page

Особенности переопределения equals

Преобразования между типами

  • Есть понятие восходящего преобразования - это означает, что объектная переменная класса-предка может содержать адрес объектной переменной класса-потомка.

class Human {
    ...
}

class Programmer extends Human {
    ...
}

class Student extends Human {
    ...
}
  • По правилу восходящего преобразования (неявное преобразование):

Programmer programmer = new Programmer(...);
Student student = new Student(...);

Human h1 = programmer;
Human h2 = student;
  • Но, есть нюанс - если мы захотим через human использовать методы/поля Programmer или Student, мы не сможем этого сделать.

  • Потому что тип Human не содержит в себе нужных методов/полей, хотя сам объект содержит:

//        System.out.println(human.getLanguage());
//        human.work();
  • Для того чтобы обратиться к этим полям/методам, необходимо сделать нисходящее преобразование (явных):

Programmer programmer1 = (Programmer) human;
programmer1.work();
  • Возможные проблемы - в переменной human может лежать не программист, а другой потомок, например Student

  • Тогда при преобразовании мы получим ошибку - ClassCastException - нельзя из Student сделать Programmer

  • Избежать такой ошибки можно с помощью предварительной проверки через instanceof:

if (human instanceof Programmer) {
            Programmer programmer1 = (Programmer) human; // нисходящее преобразование
        }
  • Т.е. мы делаем преобразование только в том случае, когда реально в переменной human лежит Programmer


Особенности переопределения equals - Преподаватель онлайн-школы AIT Marsel Sidikov

equals

  • Задача equals проверить, эквиваленты ли сами объекты между собой.

  • Сигнатура метода equals - boolean equals(Object obj)

  • Т.е. в переменную obj можно положить абсолютно любой объект, потому что Object - предок всех классов

  • Но при сравнении, мы должны иметь доступ к полям/методам, которые лежат в obj

  • Я не имею доступа к x и y потому что пока у меня на руках не Point2D, а Object

  • На помощь мне приходит нисходящее преобразование - Point2D that = (Point2D)obj

  • Чтобы исключить возможность ошибки (если вдруг у меня вместо obj лежит другой объект другого класса) - необходимо сначала сделать проверку:

if (obj instanceof Point2D)
  • Тогда у меня преобразование сработает только в том случае, когда реально в obj лежит Point2D, а не что-то другое

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

Проблема equals в потомках и предках

  • Пусть имеется следующий набор:

class A {
    private int x;
    private int y;
    
    pubic boolean equals(...) {
        return this.x == that.x && this.y == that.y;
    }
    
class B extends A {
    private int z;
}

main() {
    B b1 = new B(1, 2, 3);
    B b2 = new B(1, 2, 5);
    
    b1.equals(b2) - у нас с вами в классе B нет метода equals, поэтому вызывается отнаследованный от A метод, который сравнивает только x и y
  • Т.е. в потомке мы не сделали свой equals и получили проблему - поля, которых нет в предке просто не рассматриваются.

  • Как решить эту проблему? Надо написать свой equals внутри потомка, который сравнивает дополнительные поля потомка.

Проблема нарушения симметрии для Point2D и для Point2DColored


a ~ b -> true
b ~ a -> false
  • В случае, когда мы вызываем a.equals(b), то вызывается реализация Point2D. Далее, когда вызывается такая штука как:

obj instanceof Point2D
  • то даже в случае, когда в obj лежит Point2DColored оператор instanceof даст true, потому что Point2DColor является потомком Point2D

  • далее потомок просто повышается до предка и вы сравниваете только координаты, забыв про цвет.

  • Если координаты равны, то будет true

  • В случае, когда b.equals(a), то вызывается реализация Point2DColored.

  • Далее, когда вызывается такая штука как:

obj instanceof Point2DColored
  • то в случае, когда в obj лежит предок Point2DColored, например Point2D, то операция instanceof даст false

  • Проблема в несимметричности операции instanceof

class A

class B extends A

A a = new A();
B b = new B();

b instanceof A - true
a instanceof B - false

А как написать правильный equals?

  • Отказаться от использования instanceof

  • Заменить использованием getClass() - это метод класса Object, поэтому он есть у всех классов. Грубо говоря, этот метод возвращает название класса, которому принадлежит объект.

  • Просто добавим проверку this.getClass() == obj.getClass()

  • Важно - можно сравнивать классы через ==, поскольку в памяти они существуют в одном экземпляре и это работает быстрее.

  • На практике - используйте то, что предлагает Intellij IDEA

  • Заходим в класс, нажимаем Alt Insert, Equals And HashCode, IntelliJ IDEA Default и много enter-ов.

Когда нам понадобится хеш код?

  • Когда будем использовать HashSet и HashMap

199 просмотров

Недавние посты

Смотреть все
bottom of page