В предыдущем посте о библиотеке Lombok я описал библиотеку, которая помогает работать с шаблонным кодом на Java (и да, я знаю, что эти проблемы уже решены в Kotlin , но это реальная жизнь, и мы не можем просто сидеть и переписать каждый существующий проект, как только появится более новый или более простой язык). Но, как и многие вещи в жизни, проект Lombok имеет свои альтернативы. Давайте дадим им шанс.
Образцы кода для этой статьи можно найти здесь и здесь .
Google AutoValue
Это действительно альтернатива Lombok — потому что вы не можете использовать оба сразу. Или, по крайней мере, оказывается, что у вас возникнут трудности при использовании обоих в одном проекте с IntelliJ IDEA , который является IDE для многих и вашей по-настоящему — потому что две библиотеки по-разному обрабатывают аннотации. Таким образом, ни один из них не может жить, пока выживает другой, что примерно так прозвучало в пророчестве о Гарри Поттере и Волан-де-Морте .
Итак, мы уже знаем, как класс Person выглядел с аннотациями Lombok :
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
@Builder (toBuilder = true ) @ToString @EqualsAndHashCode @AllArgsConstructor (access = AccessLevel.PRIVATE) public class Person { @NonNull @Getter private final String lastName; @NonNull @Getter private final String firstName; @NonNull @Getter private final Integer age; } |
Если мы создадим новый проект и сделаем так, чтобы он использовал autovalue, как описано здесь , мы можем имитировать почти такую же модель с помощью AutoValue Builders .
Теперь посмотрим, как выглядит модель AutoValue :
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
package autovalue.model; import com.google.auto.value.AutoValue; @AutoValue public abstract class Person { public abstract String lastName(); public abstract String firstName(); public abstract Integer age(); public static Person create(String lastName, String firstName, Integer age) { return builder().lastName(lastName).firstName(firstName).age(age).build(); } public static Builder builder() { return new AutoValue_Person.Builder(); } @AutoValue .Builder public abstract static class Builder { public abstract Builder lastName(String lastName); public abstract Builder firstName(String firstName); public abstract Builder age(Integer age); public abstract Person build(); } } |
То, что вы можете видеть, это определенно больше кода .
В то время как Lombok генерирует компоновщик с одной аннотацией, AutoValue заставит вас создать свой собственный код компоновщика — но не все. По сути, вы определяете свои интерфейсы, а реализация остается за кодом, сгенерированным AutoValue , вам не нужно фактически реализовывать код, который находится в методах получения и установки. Даже если мы согласимся с тем, что интерфейсы получения AutoValue не будут занимать намного больше времени или пространства, чем определения полей Lombok , для некоторых людей написание кода построителя AutoValue может все еще быть хлопотным и неприятным.
Однако это обеспечивает большую гибкость , потому что вы можете изменить имена методов компоновщика. Кроме того, большой выигрыш — это анализ кода и поиск использования — таким образом, вы можете фактически искать использование фактических методов получения и установки отдельно, что также может быть важно для разработчиков.
Экземпляр создается так же, как и в Lombok .
1
2
3
4
5
|
final Person anna = Person.builder() .age( 31 ) .firstName( "Anna" ) .lastName( "Smith" ) .build(); |
Все наши тесты выполняются с минимальными изменениями кода, в основном потому, что AutoValue не имеет способа преобразовать экземпляр в конструктор (или, по крайней мере, я не мог его легко найти), поэтому копирование — это просто вызов статического метода фабрики:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
package autovalue.model; import org.junit.Test; import static org.assertj.core.api.Java6Assertions.assertThat; public class PersonTest { private static Person JOHN = Person.builder() .firstName( "John" ) .lastName( "Doe" ) .age( 30 ) .build(); private static Person JANE = Person.builder() .firstName( "Jane" ) .lastName( "Doe" ) .age( 30 ) .build(); @Test public void testEquals() throws Exception { Person JOHN_COPY = Person.create(JOHN.lastName(), JOHN.firstName(), JOHN.age()); assertThat(JOHN_COPY).isEqualTo(JOHN); } @Test public void testNotEquals() throws Exception { assertThat(JANE).isNotEqualTo(JOHN); } @Test public void testHashCode() throws Exception { Person JOHN_COPY = Person.create(JOHN.lastName(), JOHN.firstName(), JOHN.age()); assertThat(JOHN_COPY.hashCode()).isEqualTo(JOHN.hashCode()); } @Test public void testHashCodeNotEquals() throws Exception { Person JOHN_COPY = Person.create(JOHN.lastName(), JOHN.firstName(), JOHN.age()); assertThat(JOHN_COPY.hashCode()).isNotEqualTo(JANE.hashCode()); } @Test public void testToString() throws Exception { String jane = JANE.toString(); assertThat(jane).contains(JANE.lastName()); assertThat(jane).contains(JANE.firstName()); assertThat(jane).contains( "" + JANE.age()); assertThat(jane).doesNotContain(JOHN.firstName()); } } |
Другие отличия, которые сразу очевидны:
- Написанные вами классы AutoValue всегда абстрактны. Они реализованы в сгенерированном коде AutoValue .
- Классы AutoValue являются автоматически неизменяемыми. Есть обходной путь для того, чтобы они имели свойства неизменяемых типов . Даже если вы явно хотели, чтобы в ваших экземплярах были сеттеры, это невозможно .
Почему вы должны использовать AutoValue ? Создатели AutoValue позаботились о том, чтобы описать здесь достижения библиотеки и даже создать целую презентацию об этом .
Библиотека неизменных
Библиотека также использует процессоры аннотаций Java для создания простых, безопасных и согласованных объектов значений. Ну так же, как и предыдущие два. Что еще нового? Давайте посмотрим.
Простейший класс значений будет выглядеть следующим образом.
01
02
03
04
05
06
07
08
09
10
|
package immutables.model; import org.immutables.value.Value; @Value .Immutable public abstract class Person { public abstract String lastName(); public abstract String firstName(); public abstract Integer age(); } |
Таким образом, существует тот же принцип наличия абстрактных классов, который реализован только в сгенерированном коде. Для этого вам нужно включить процессоры аннотаций IDE , так же, как вы делаете это для Lombok (но не для AutoValue , как там, где это делается плагином Gradle).
Как выглядит создание объекта?
1
2
3
4
5
6
|
final Person anna = ImmutablePerson.builder() .age( 31 ) .firstName( "Anna" ) .lastName( "Smith" ) .build(); System.out.println(anna); |
Наиболее очевидные различия на первый взгляд:
- Мы не объявляем методы построения.
- Статические методы компоновщика / фабрики создаются не в нашем собственном классе, а в сгенерированном.
- Как и AutoValue, нет способа генерировать сеттеры для класса, только для компоновщика.
- Сгенерированный класс также автоматически добавляет с -ers, то есть методами экземпляра, которые позволяют создать копию экземпляра, изменив одно свойство:
1
2
3
4
5
6
7
8
9
|
final ImmutablePerson anna = ImmutablePerson.builder() .age( 31 ) .firstName( "Anna" ) .lastName( "Smith" ) .build(); System.out.println(anna); final ImmutablePerson annaTheSecond = anna.withAge( 23 ).withLastName( "Smurf" ); System.out.println(annaTheSecond); |
- У построителя есть автоматически добавленный метод from () , который позволяет создать точную копию экземпляра, а также сгенерированный статический метод copyOf () для сгенерированного класса:
1
2
3
|
Person JOHN_COPY = ImmutablePerson.builder().from(JOHN).build(); // OR Person JOHN_COPY = ImmutablePerson.copyOf(JOHN); |
И снова наш тест выполняется с минимальными изменениями, которые в основном касаются того, как мы копируем экземпляры:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
package immutables.model; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; public class PersonTest { private static Person JOHN = ImmutablePerson.builder() .firstName( "John" ) .lastName( "Doe" ) .age( 30 ) .build(); private static Person JANE = ImmutablePerson.builder() .firstName( "Jane" ) .lastName( "Doe" ) .age( 30 ) .build(); @Test public void testEquals() throws Exception { //ImmutablePerson JOHN_COPY = ImmutablePerson.builder().from(JOHN).build(); Person JOHN_COPY = ImmutablePerson.copyOf(JOHN); assertThat(JOHN_COPY).isEqualTo(JOHN); } @Test public void testNotEquals() throws Exception { assertThat(JANE).isNotEqualTo(JOHN); } @Test public void testHashCode() throws Exception { Person JOHN_COPY = ImmutablePerson.copyOf(JOHN); assertThat(JOHN_COPY.hashCode()).isEqualTo(JOHN.hashCode()); } @Test public void testHashCodeNotEquals() throws Exception { Person JOHN_COPY = ImmutablePerson.copyOf(JOHN); assertThat(JOHN_COPY.hashCode()).isNotEqualTo(JANE.hashCode()); } @Test public void testToString() throws Exception { String jane = JANE.toString(); assertThat(jane).contains(JANE.firstName()); assertThat(jane).contains(JANE.lastName()); assertThat(jane).contains( "" + JANE.age()); assertThat(jane).doesNotContain(JOHN.firstName()); } } |
О библиотеке Immutables можно сказать гораздо больше, так что здесь есть довольно большое руководство . Здесь, в этой статье, мы только немного поцарапали поверхность. Например, есть намного больше деталей о сериализации JSON с Immitables и настройках стиля (префиксы методов, имена сборщиков и т. Д.) И даже о генерации репозитория для Mongo, чтобы документы можно было рассматривать как неизменяемые . Но это гораздо больше, чем я хочу затронуть в этой простой статье.
Вывод: одна из проблем неиссякаемого, но все же языка Java — это многословность и шаблонный код. Но есть множество инструментов, чтобы справиться с этим, и можно выбрать библиотеку, которая подходит лучше всего, вместо того, чтобы кодировать путем копирования-вставки или пытаться написать свой собственный генератор кода.
Используйте их хорошо.
Используйте это хорошо.
Опубликовано на Java Code Geeks с разрешения Марины Чернявской, партнера нашей программы JCG . Смотрите оригинальную статью здесь: Lombok, AutoValue и Immutables, или Как писать меньше и лучше возвращать код
Мнения, высказанные участниками Java Code Geeks, являются их собственными. |