Какие контракты нужно соблюдать при переопределении hashCode() с помощью Lombok?

«Какие контракты нужно соблюдать при переопределении hashCode() с помощью Lombok?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

При генерации hashCode() через Lombok (@EqualsAndHashCode) критически важно соблюдать его контракт, особенно связь с equals():

Основной контракт (из Object.hashCode()):

  1. Согласованность с equals(): Если x.equals(y) == true, то x.hashCode() == y.hashCode() обязательно.
  2. Обратное не обязательно: Равные хеш-коды не гарантируют равенство объектов (возможна коллизия).
  3. Согласованность во времени: Хеш-код должен оставаться постоянным для неизменяемого объекта в рамках одного выполнения приложения.

Практические рекомендации для Lombok:

  • Lombok гарантирует пункт 1, генерируя оба метода из одного набора полей.
  • Используйте неизменяемые или стабильные поля. Если поле, участвующее в hashCode(), может измениться, объект станет «невидим» для хеш-коллекций.
    @EqualsAndHashCode
    public class BadExample {
        private List<String> tags; // Изменение списка сломает хеш-код!
    }
  • Для изменяемых полей используйте onlyExplicitlyIncluded = true или @EqualsAndHashCode.Exclude.

Пример безопасного подхода:

@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class User {
    @EqualsAndHashCode.Include
    private final Long id; // Идеальный кандидат: неизменяемый и уникальный

    private String name; // Не участвует, может меняться
}

Почему важно: Нарушение контракта hashCode()/equals() приводит к потере объектов в HashSet или HashMap.