Зарегистрируйтесь, чтобы продолжить обучение

Сравнение объектов Java: Классы

Прямое сравнение объектов в Java почти всегда (кроме строк) будет возвращать false для разных объектов, даже если их содержимое идентично:

var u1 = new User("Mike");
var u2 = new User("Anna");

u1 == u2; // false

Иногда необходимо другое поведение, например, сравнение на основе каких-то значений. Пример с городами:

var city1 = new City("London");
var city2 = new City("London");

// А мы хотим true
city1 == city2; // false

equals()

Эта задача тесно связана с классом Object. Именно в нем реализован механизм, который мы используем для сравнения строк.

str1.equals(str2);

В случае строк equals() сравнивает сами строки. Для всех остальных типов объектов, стандартная реализация возвращает true только в том случае, если объект сравнивается сам с собой. Это поведение идентично сравнению через ==.

var city1 = new City("London");
var city2 = new City("London");

// По умолчанию поведение equals и == одинаково
// для всех объектов кроме строк
city1 == city2; // false
city1.equals(city2); // false

Но это поведение можно изменить так как нам нужно, благодаря возможности переопределить этот метод в нужном классе. Предположим что у нас есть класс User с полями firstName и age. Его реализация:

public class User {
    private String firstName;
    private int age;

    public User(String firstName, int age) {
        this.firstName = firstName;
        this.age = age;
    }

    // Геттеры и сеттеры
}

Допустим мы хотим сравнивать пользователей на основе равенства их firstName и возраста. Если имя и возраст одинаковые, то мы считаем, что это тот же самый пользователь. Реализовать эту проверку можно напрямую:

var u1 = new User("Nika", 22);
var u2 = new User("David", 22);
var u3 = new User("Nika", 22);

u1.getFirstName().equals(u2.getFirstName()) &&
    u1.getAge() == u2.getAge()); // false

u1.getFirstName().equals(u3.getFirstName()) &&
    u1.getAge() == u3.getAge()); // true

Но нам бы хотелось сравнивать через метод equals, что значительно упрощает процесс, особенно если проверок по коду много.

u1.equals(u2); // false
// Чтобы так заработало, нужно переопределить equals
// на сравнение firstName
u1.equals(u3); // true

Чтобы иметь возможность сравнивать пользователей на основе их firstName и возраста, нам понадобится реализовать метод equals() с таким содержимым:

class User {
    // Должен работать для любых объектов, которые передаются во внутрь
    // поэтому тип входного параметра Object
    @Override
    public boolean equals(Object obj) {
        // Если объект сравнивается сам с собой
        if (this == obj) {
            return true;
        }

        // Проверяем что объект того же класса
        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }

        // Приводим тип к нужному, в данном случае User
        User person = (User) obj;

        // Сравниваем нужные поля
        return firstName.equals(person.firstName) && age == person.age;
    }

    // Остальной код класса

}

Большая часть этого кода идентична для всех классов. Разница появляется только в самом конце, где мы выбираем по каким полям происходит сравнение и то как это делается.

hashCode()

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

Для класса User, объекты которого сравниваются на основе firstName и age его реализация может быть такой:

@Override
public int hashCode() {
    return Objects.hash(firstName, age);
}

Статический метод Objects.hash() возвращает уникальный (с оговорками) числовой код для всех переданных в него параметров. То есть ровно то, что требуется от метода hashCode()

Вывод

Сравнение объектов в Java реализуется с помощью методов equals() и hashCode(). Основные правила при работе с equals() и hashCode() звучат так:

  • Если переопределяется equals(), то должен переопределяться hashCode().
  • Одинаковые объекты должны возвращать одинаковый хеш-код.
  • Разные объекты могут возвращать одинаковый хеш-код.

Дополнительные материалы

  1. Метод hash() класса Objects
  2. Метод getClass()

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы
профессия
от 25 000 ₸ в месяц
Разработка приложений на языке Java
10 месяцев
с нуля
Старт 26 декабря

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»