Какой контракт между методами equals() и hashCode() в Java?

«Какой контракт между методами equals() и hashCode() в Java?» — вопрос из категории Java, который задают на 24% собеседований AQA / Automation. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Контракт между equals() и hashCode(), определенный в Java, гласит:

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

Почему это критично для QA-инженера? Нарушение этого контракта приводит к некорректной работе hash-коллекций (HashMap, HashSet, HashTable). Объект, добавленный в HashSet, может в нем не найтись, что вызывает трудноуловимые баги.

Пример корректной реализации для класса User:

import java.util.Objects;

public class User {
    private final Long id;
    private final String name;

    // Конструктор, геттеры...

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(id, user.id) && Objects.equals(name, user.name);
    }

    @Override
    public int hashCode() {
        // Используем вспомогательный метод Objects.hash() для согласованного вычисления
        return Objects.hash(id, name);
    }
}

На практике: При написании или ревью кода, где классы используются как ключи в HashMap или элементы в HashSet, я всегда проверяю, что оба метода переопределены согласованно.