Construtor Lombok com valor padrão
1. Introdução
Neste tutorial rápido, vamos investigar como podemos fornecer valores padrão para atributos ao usar o padrão do construtor comLombok.
Certifique-se de verificar nossointro to Lombok também.
2. Dependências
UsaremosLombok neste tutorial e, para isso, precisamos apenas de uma dependência:
org.projectlombok
lombok
1.16.18
provided
3. POJO com Lombok Builder
Primeiro, vamos dar uma olhada em como o Lombok pode nos ajudar a nos livrar do código clichê necessário para implementar o padrão do construtor.
Começaremos com um POJO simples:
public class Pojo {
private String name;
private boolean original;
}
Para esta classe ser útil, precisamos de getters. Além disso, por exemplo, se quisermos usar esta classe com um ORM, provavelmente precisaremos de um construtor padrão.
Além disso, queremos um construtor para esta classe. Com o Lombok, podemos ter tudo isso com algumas anotações simples:
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Pojo {
private String name;
private boolean original;
}
4. Definição de expectativas
Vamos definir algumas expectativas para o que queremos alcançar na forma de testes de unidade.
O primeiro requisito básico é a presença de valores padrão depois que construímos um objeto com um construtor:
@Test
public void givenBuilderWithDefaultValue_ThanDefaultValueIsPresent() {
Pojo build = Pojo.builder()
.build();
Assert.assertEquals("foo", build.getName());
Assert.assertTrue(build.isOriginal());
}
Of course, this test fails since the @Builder annotation doesn’t populate values. Vamos corrigir isso em breve.
Se usarmos um ORM, ele geralmente conta com um construtor padrão. Portanto, devemos esperar o mesmo comportamento do construtor padrão que o construtor:
@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());
}
Nesta fase, este teste passa.
Agora vamos ver como podemos fazer os dois testes passarem!
5. AnotaçãoBuilder.Default de Lombok
Desde o Lombok v1.16.16, podemos usar a anotação interna de@Builder:
// class annotations as before
public class Pojo {
@Builder.Default
private String name = "foo";
@Builder.Default
private boolean original = true;
}
É simples e legível, mas tem algumas falhas.
Com isso, os valores padrão estarão presentes com o construtor, fazendo com que o primeiro caso de teste seja aprovado. Unfortunately, the no-args constructor won’t get the default values, making the second test case fail. Mesmo se o construtor no-args não for gerado, mas explicitamente escrito.
Este efeito colateral da anotaçãoBuilder.Default está presente desde o início e provavelmente continuará por muito tempo.
6. Inicialize o Builder
Podemos tentar fazer os dois testes passarem definindo valores padrão em uma implementação de construtor minimalista:
// 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;
}
}
Dessa forma, ambos os testes serão aprovados.
Infelizmente, o preço é duplicação de código. For a POJO with tens of fields, it could be error prone to maintain the double initialization.
Mas, se estamos dispostos a pagar esse preço, devemos cuidar de mais uma coisa também. Se renomearmos nossa classe usando uma refatoração em nosso IDE, a classe interna estática não será renomeada automaticamente. Então, o Lombok não o encontrará e nosso código quebrará.
Para eliminar esse risco, podemos decorar a anotação do construtor:
// 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. UsandotoBuilder
@Builder also suporta a geração de uma instância do construtor a partir de uma instância da classe original. Este recurso não está ativado por padrão. Podemos ativá-lo definindo o parâmetrotoBuilder na anotação do construtor:
// class annotations as before
@Builder(toBuilder = true)
public class Pojo {
private String name = "foo";
private boolean original = true;
}
Com isso,we can get rid of the double initialization.
Claro, há um preço por isso. We have to instantiate the class to create a builder. Portanto, temos que modificar nossos testes também:
@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());
}
Novamente, ambos os testes foram aprovados, então temos o mesmo valor padrão usando o construtor no-args e usando o construtor.
8. Conclusão
Portanto, vimos várias opções para fornecer valores padrão para o construtor Lombok.
Vale a pena observar o efeito colateral da anotaçãoBuilder.Default. Mas, as outras opções também têm suas desvantagens. Portanto, temos que escolher cuidadosamente com base na situação atual.
Como sempre, o código está disponívelover on GitHub.