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

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

Ответ

При использовании Lombok (@EqualsAndHashCode) для генерации equals() и hashCode() необходимо соблюдать общий контракт Object.equals():

  1. Рефлексивность: x.equals(x) всегда true.
  2. Симметричность: Если x.equals(y) == true, то y.equals(x) также должно быть true.
  3. Транзитивность: Если x.equals(y) и y.equals(z), то x.equals(z).
  4. Согласованность: Многократные вызовы x.equals(y) должны возвращать одно и то же значение, если поля, участвующие в сравнении, не менялись.
  5. Сравнение с null: x.equals(null) всегда false.

Особенности Lombok:

  • Аннотация @EqualsAndHashCode по умолчанию включает все нестатические, нетранзиентные поля.
  • Ключевое правило: Lombok автоматически обеспечивает согласованность с hashCode(), генерируя оба метода на основе одного набора полей.

Управление полями:

// Включить только явно помеченные поля
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class User {
    @EqualsAndHashCode.Include
    private Long id; // Участвует в equals/hashCode

    private String name; // НЕ участвует

    @EqualsAndHashCode.Exclude
    private String email; // Явно исключено
}

Почему важно: Нарушение контракта, особенно симметричности и транзитивности, приводит к непредсказуемому поведению коллекций (HashSet, HashMap).