Introduction
Project Lombok will be able to generate the builder pattern class to make a java bean as immutable. But for some use cases you might need to add custom property validations while building an object. Lets go through lombok features to see how it can help us solve this.
See It In Action
Lombok doesn’t generate any code if you implement something to complement with it’s code generation like getters, setter or any other methods. Lets use this feature to solve the custom validations within builders.
Lets say we have a Customer
class like this.
public class Customer {
private long id;
private String name;
}
Now we would like to make it as an immutable class with builder pattern so lombok can generate it for us. Inorder to do this we need to add @Builder
annotation for this class.
@Getter
@EqualsAndHashCode
@ToString
@Builder(builderClassName = "Builder")
public class Customer {
private long id;
private String name;
}
We have a builder ready for us now but without any validations of Customer
properties. Lets imagine for our case we need to add validations like id
cannot negative or zero and name
cannot be null or empty.
@Getter
@EqualsAndHashCode
@ToString
@Builder(builderClassName = "Builder",buildMethodName = "build")
public class Customer {
private long id;
private String name;
static class Builder {
Customer build() {
if (id < 0) {
throw new RuntimeException("Invaid id");
}
if (Objects.isNull(name)) {
throw new RuntimeException("name is null");
}
return new Customer(id, name);
}
}
}
Remeber we said that lombok won’t generate the code which we implemented for, here we have implemented the build
method inside the Builder
class and we specified that use the build
as buildMethodName
using lombok annotations so lombok won’t generate the build
method instead simply use the one we implemented.
Lets write some tests to make sure this will work.
public class CustomerTest {
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Test
public void testValidCustomer() {
Customer.Builder customer = Customer.builder();
customer.id(100);
customer.name("bob");
Assert.assertNotNull("Customer not built", customer.build());
}
@Test
public void testInvalidCustomer() {
expectedException.expect(RuntimeException.class);
expectedException.expectMessage("name is null");
Customer.Builder customer = Customer.builder();
customer.id(100);
Assert.assertNull("Customer shouldn't built", customer.build());
}
}
I have two tests testValidCustomer
for valid customer and testInvalidCustomer
for invalid where not providing the name, both tests passed as expected.
This way we can add any type of validations we want with lombok with eliminating lot of boilerplate code. There are other alternatvies for this type of use cases which eliminate boilerplate code like immutables, AutoValue etc…