It’s not a secret that Java is quite verbose and will require a developer to often write significantly more code for the same task than other languages. To address this problem, we’ve mentioned a library called Lombok on the codecentric blog in the past – see here and here . In short, it’s a code generation library that provides a set of annotations you can use to drastically reduce boilerplate code in your applications. I’ve personally used it with great success on a number of occasions and since the topic came up in my current project I wanted to elaborate on it bit more and address a few problems I was confronted with. As we’ve covered the basics before, let me get right to a few specific features and topics that I find noteworthy on top of that.
Using @Builder
For some time now, Lombok provides an annotation for implementing the Builder pattern on your classes. Doing this manually is a good example of Java’s verbosity:
1@Getter
2@EqualsAndHashCode
3@AllArgsConstructor
4public class Person {
5 private String firstname;
6 private String lastname;
7 private String email;
8
9 public static Builder builder() {
10 return new Builder();
11 }
12
13 public static class Builder {
14
15 private String firstname;
16 private String lastname;
17 private String email;
18
19 public Builder fistname(String firstname) {
20 this.firstname = firstname;
21 return this;
22 }
23
24 public Builder lastname(String lastname) {
25 this.lastname = lastname;
26 return this;
27 }
28
29 public Builder email(String email) {
30 this.email = email;
31 return this;
32 }
33
34 public Person build() {
35 return new Person(firstname, lastname, email);
36 }
37 }
38}
With every additional property this code will grow significantly. There’s more sophisticated builder implementations that will for example guarantee that mandatory values are set during the construction of an object, but in my experience, most implementations of the builder pattern look like my example above. Let’s see how Lombok helps:
1@Getter
2@EqualsAndHashCode
3@AllArgsConstructor
4@Builder
5public class Person {
6 private final String firstname;
7 private final String lastname;
8 private final String email;
9}
That’s it! One line and you have the same implementation as shown before. There’s some parameters that you can use to customize the generated builder. @Builder(toBuilder=true) will generate a toBuilder() method that will copy the contents of an existent Person instance to a builder, for example. That’s useful if you want to copy and change an object.
Other libraries have been doing builder generation before Lombok, but I know of none that integrate as smoothly. PojoBuilder – for example – will create seperate class files in a folder that you have to add to your project’s classpath. In contrast, Lombok hooks into the compile phase and will change the abstract syntax tree of the target class itself.
As with anything, the example case above looks intriguing but once you start working seriously, you often encounter edge cases and all kinds of problems. Generally, my experience has been very positive, but when working with the @Builder pattern I actually had a few problems to solve.
@Builder and generics
When I first put @Builder on a generic class I was confronted with a compiler error.
1@Builder
2public class Response {
3 private T body;
4}
5
6Response<String> response = Response.builder().body("body").build();
The compiler complains about an incompatible assignment, as the result of the build process is Response. What’s required is a hint for the compiler when creating the builder, you’ll have to specify the requested type explicitly when creating the builder: Sometimes you use @Builder on a class that inherits from a parent class. Lombok will not consider fields from the superclass in the generated builder class. There’s a workaround, though. Normally, you use @Builder as a type annotation, but you can also use it on constructors and methods. What you can do in this case is create a constructor that takes all the arguments that are required for your class (including the ones for the superclass) and then place @Builder on the constructor. You’ll get a complete builder and can use it like this: In the context of dependency injection I like to use constructors to pass dependencies into objects: I find it unreasonable to create incomplete objects and to have dependencies set afterwards. In order to use constructor injection, you often have to be able to annotate a constructor. How do you do this if you have Lombok generate your constructors? It turns out, there is an experimental feature that can help you with this: Lombok will then add the provided annotation to the generated constructor. You’re right, the syntax looks a bit funny (see the small print at the bottom of the feature documentation for details). And because of the way it is implemented Lombok makes it clear, that this is experimental and might change or disappear in the future. If you can live with that, than it will enable you to combine Lombok and constructor injection (as well as a few other things). If not, you can always choose to not use Lombok for these constructors, of course. Integrating Lombok into your project is quite easy: For one thing, you need to have Lombok on the project’s classpath in order to get a build working. But equally important is integration with your IDE. I’ve been using both Eclipse and Intellij when working with Lombok, but there are other integrations as well. Again, the Lombok website gives a good overview about what has to be done: For Eclipse, you run the Lombok jar as a java application and tell it about the location of your Eclipse installation, for Intellij there’s a Plugin that you can install via the plugin repository. The best code that you can write is the code that you don’t write. Lombok is tremendously useful, it will help you to trim your codebase and focus on the important parts of your applications. I’ve been using it for a few years now and I have not experienced any real problems, so far. I recommend you try it yourself!1Response<String> response = Response.<String>builder().body("body").build();
@Builder and inheritance
1@AllArgsConstructor
2public class Parent {
3 private String a;
4}
5
6public class Child extends Parent {
7
8 private String b;
9
10 @Builder
11 public Child(String a, String b){
12 super(a);
13 this.b = b;
14 }
15}
1Child.builder().a("testA").b("testB").build();
Lombok and constructor injection
1@AllArgsConstructor(onConstructor = @__(@Autowired) )
2public class HelloLombok {
3
4 public Dependency dependency;
5}
Integrating Lombok
More articles
fromReinhard Prechtl
Your job at codecentric?
Jobs
Agile Developer und Consultant (w/d/m)
Alle Standorte
More articles in this subject area
Discover exciting further topics and let the codecentric world inspire you.
Gemeinsam bessere Projekte umsetzen.
Wir helfen deinem Unternehmen.
Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.
Hilf uns, noch besser zu werden.
Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.
Blog author
Reinhard Prechtl
Senior IT Consultant
Do you still have questions? Just send me a message.
Do you still have questions? Just send me a message.