equals(), toString() и сущности.

resized_inceptius-meme-generator-we-have-to-go-deeper-014848О том, как замечательно project  сам генерирует геттеры/сеттеры и конструкторы, я уже писал. Но у класса Object есть ещё методы и их тоже можно переопределять автоматически.

equals() и hashCode()

Эти методы глубоко пересечены, поэтому генерируются вдвоём, аннотацией  @EqualsAndHashCode

Параметр exclude указывает, какие поля необходимо исключить из сгенерированных функций. По умолчанию так же исключаются все static и transient поля класса. Можно и наоборот, указать список полей, требующих включения, перечислив их в параметре of.

Аннотация  @EqualsAndHashCode имеет ещё два параметра: первый, doNotUseGetters, указывает, как методы equals() и hashCode() должны получать данные, обращаясь напрямую к полям класса или вызывая соответствующие геттеры. Второй параметр, callSuper, гораздо интереснее — он указывает, надо ли при генерации методов equals() и hashCode() вызывать в них соответствующие методы базового класса.

Кроме того,  @EqualsAndHashCode генерирует метод canEqual(), проверяющий, можно ли вообще сравнивать эти объекты. Потребность в canEqual() возникает из-за наследования классов: предположим у нас есть класс Point и класс ColoredPoint extends Point. Очевидно, что если мы сравним экземпляр ColoredPoint с экземпляром Point, они будут не эквивалентны. Но. Если мы сравним наоборот, экземпляр Point с ColoredPoint, то они внезапно станут эквивалентны, потому что Point сравнивает только те данные, о которых знает.

canEqual() проверяет, совпадает ли тип объектов и корректно ли их сравнивать с учётом  их положения в иерархии наследования.

toString()

Но вернёмся к lombok. В тесте класса User выводится результат toString():

Такой красивый toString() генерирует аннотация @ToString, которая принимает те же самые параметры что и @EqualsAndHashCode и которые, очевидно, имеют точно такую же смысловую нагрузку. В поведении @ToString есть одно отличие — transient  поля по умолчанию включены в вывод.

@Data

Но всё таки, даже с учётом аннотаций от project lombok, код класса User выглядит перегруженным. На простой класс с тремя полями у нас 10(!) аннотаций. Может быть, можно с этим что-то сделать? Аннотация @Data заменяет (почти) все вышеперечисленные аннотации! Она добавляет @Getter/@Setter ко всем полям, добавляет @EqualsAndHashCode и @ToString и создаёт конструктор для final  и @NonNull полей. Поведение по умолчанию всех вышеперечисленных аннотаций сохраняется: геттеры/сеттеры будут публичными, static поля будут исключены из equals(), hashCode() и toString(), а transient поля будут исключены из equals() и hashCode().  Аннотации можно передать параметр of, чтобы сгенерировать статический метод для создания класса:

@Value и @Wither

@Value это @Data для неизменяемых (immutable) классов. Вместо того, чтобы художественно расставлять руками  final, просто пишем @Value и получаем класс, в котором все поля private final, для них сгенерированны геттеры, equals(), hashCode(), toString() и конструктор, который тоже можно заменить статическим методом.

Сеттеров такому классу, конечно же, не полагается. Но зато у него есть @Wither, который как сеттер, только для неизменяемых классов. Для переменных с аннотацией @Wither будет сгенерирован ммм виззер??? который вернёт копию объекта, с новым значением переменной:

Код примера, за исключением Point и ColoredPoint, доступен на github.