Lombok Builder със стойност по подразбиране

1. Въведение

В този бърз урок ще проучим как можем да предоставим стойности по подразбиране за атрибутите, когато използваме шаблона на конструктора с Lombok .

Не забравяйте да разгледате и нашето въведение в Ломбок.

2. Зависимости

Ще използваме Lombok в този урок и за това ни е необходима само една зависимост:

 org.projectlombok lombok 1.18.10 provided 

3. POJO с Lombok Builder

Първо, нека да разгледаме как Lombok може да ни помогне да се отървем от кода на шаблона, необходим за реализиране на шаблона на конструктора.

Ще започнем с просто POJO:

public class Pojo { private String name; private boolean original; }

За да бъде този клас полезен, ще ни трябват гетери. Също така, например, ако искаме да използваме този клас с ORM, вероятно ще се нуждаем от конструктор по подразбиране.

На всичко отгоре, ние искаме строител за този клас. С Lombok можем да получим всичко това с няколко прости анотации:

@Getter @Builder @NoArgsConstructor @AllArgsConstructor public class Pojo { private String name; private boolean original; }

4. Определяне на очакванията

Нека дефинираме някои очаквания за това, което искаме да постигнем под формата на единични тестове.

Първото и основно изискване е наличието на стойности по подразбиране, след като изградим обект със строител:

@Test public void givenBuilderWithDefaultValue_ThanDefaultValueIsPresent() { Pojo build = Pojo.builder() .build(); Assert.assertEquals("foo", build.getName()); Assert.assertTrue(build.isOriginal()); }

Разбира се, този тест е неуспешен, тъй като анотацията @Builder не попълва стойности. Скоро ще оправим това.

Ако използваме ORM, той обикновено разчита на конструктор по подразбиране. И така, трябва да очакваме същото поведение от конструктора по подразбиране, както и от конструктора:

@Test public void givenBuilderWithDefaultValue_NoArgsWorksAlso() { Pojo build = Pojo.builder() .build(); Pojo pojo = new Pojo(); Assert.assertEquals(build.getName(), pojo.getName()); Assert.assertTrue(build.isOriginal() == pojo.isOriginal()); }

На този етап този тест преминава.

Сега нека видим как можем да направим и двата теста да преминат!

5. Конструктор на Ломбок. Анотация по подразбиране

Тъй като Lombok v1.16.16, можем да използваме вътрешната анотация на @Builder :

// class annotations as before public class Pojo { @Builder.Default private String name = "foo"; @Builder.Default private boolean original = true; }

Той е прост и четим, но има някои недостатъци.

С това стойностите по подразбиране ще присъстват в конструктора, което прави първия тест да премине. За съжаление конструкторът no-args няма да получи стойностите по подразбиране, което прави втория тестов случай неуспешен . Дори ако конструкторът no-args не е генериран, а е изрично написан.

Този страничен ефект на Builder.Default анотацията присъства от самото начало и вероятно ще бъде с нас дълго време.

6. Инициализирайте Builder

Можем да опитаме да направим двата теста да преминат, като дефинираме стойности по подразбиране в минималистично изпълнение на конструктор:

// class annotations as before public class Pojo { private String name = "foo"; private boolean original = true; public static class PojoBuilder { private String name = "foo"; private boolean original = true; } }

По този начин и двата теста ще преминат.

За съжаление цената е дублиране на код. За POJO с десетки полета може да има грешка при поддържане на двойната инициализация.

Но ако сме готови да платим тази цена, трябва да се погрижим и за още нещо. Ако преименуваме нашия клас с помощта на рефакторинг в нашата IDE, статичният вътрешен клас няма да бъде преименуван автоматично. След това Ломбок няма да го намери и нашият код ще се счупи.

За да премахнем този риск, можем да украсим анотацията на строителя:

// class annotations as before @Builder(builderClassName = "PojoBuilder") public class Pojo { private String name = "foo"; private boolean original = true; public static class PojoBuilder { private String name = "foo"; private boolean original = true; } }

7. Използване на toBuilder

@Builder също поддържа генериране на екземпляр на конструктора от екземпляр на оригиналния клас. Тази функция не е активирана по подразбиране. Можем да го активираме, като зададем параметъра toBuilder в анотацията на конструктора:

// class annotations as before @Builder(toBuilder = true) public class Pojo { private String name = "foo"; private boolean original = true; }

С това можем да се отървем от двойната инициализация .

Разбира се, има цена за това. Трябва да създадем екземпляр на класа, за да създадем конструктор. Така че, ние също трябва да модифицираме нашите тестове:

@Test public void givenBuilderWithDefaultValue_ThenDefaultValueIsPresent() { Pojo build = new Pojo().toBuilder() .build(); Assert.assertEquals("foo", build.getName()); Assert.assertTrue(build.isOriginal()); } @Test public void givenBuilderWithDefaultValue_thenNoArgsWorksAlso() { Pojo build = new Pojo().toBuilder() .build(); Pojo pojo = new Pojo(); Assert.assertEquals(build.getName(), pojo.getName()); Assert.assertTrue(build.isOriginal() == pojo.isOriginal()); }

Отново и двата теста преминават, така че имаме същата стойност по подразбиране, използвайки конструктора no-args, както когато използваме конструктора.

8. Заключение

И така, разгледахме няколко опции за предоставяне на стойности по подразбиране за Lombok builder.

Страничният ефект на Builder . Анотацията по подразбиране си струва да се следи. Но и другите опции имат своите недостатъци. Затова трябва да избираме внимателно въз основа на текущата ситуация.

Както винаги, кодът е достъпен в GitHub.