1. Introduction
Java is a very solid, robust, and matured and one of the widely used programming language with a strong community support.
However one of the criticism points it has drawn is its very verbose than compared to its counterparts like Python, Ruby. The best example being Hello world example (In java it’s done in 5-6 lines whereas it’s a simple 1 liner code in python). Similarly reading a file (java takes 10+ lines for it while it’s again a 1 liner job in python).
In this article, we’ll explore Project Lombok - a java library which attempts to target this area of Java.
Lombok is a build time dependency, simply write your minified java code using the Lombok’s annotations, while compiling the java file, the annotations are removed and replaced by its desired bytecode. Thus the .class file will be same to one which we would have got after writing java code the normal way.
Following are salient features of Lombok:
- Homepage - https://projectlombok.org/
- Lombok also integrates well with our IDEs like Eclipse, IntelliJ, Netbeans.
Adding Lombok Dependency:
In order to use Lombok in our maven project, simply include its dependency.
2. Simplifying POJOS:
A POJO (Plain Old Java Object) is defined as Java class having private attributes, public getters and setters and a default, no-arg constructor. This class is simple as it gets and is not ties to any framework (Eg a class implementing a interface like JMS).
By employing POJOS, code is much simpler. This lends itself to better testing, flexibility, and extensibility. This notion is promoted widely by a lot of frameworks like Spring.
However while writing a simple POJO, we all have gone through the pain of generating the getters/setters, overriding the hashCode and equals method and finally overriding implementation of toString. All modern IDEs provide generators for the same.
2.1 Simplifying getters/setters/toString/equals:
Consider a sample User class with 5 attributes. Also 2 objects of User are equal if they have same id. The code for this simple class is as follows :
The resultant code spans around 80 lines.
Although it’s easily generated by IDE, the issue is with code maintainability. Adding a field invites adding its getters/ setters too.
With lombok the class becomes as simple as:
The generated .class file of this User.java and earlier User.java is similar which means Lombok does its job exceptionally. Our User.java class looks more elegant and we no longer have to do the repetitive tasks of creating getters/setters/equals/hashcode and toString methods.
Following is the significance of the Lombok’s annotations used above :
No | Annotation | Description |
---|---|---|
1 | @Getter | Generates getters methods for all the attributes. They as generated as per POJO convention and in Camel-case Eg : public Integer getId() { return id; } public String getFirstName() { returnfirstName; } |
2 | @Setter | Generates setters methods for all the attributes. They as generated as per POJO convention and in Camel-case |
3 | @NoArgsConstructor | Creates a no-argument constructor publicUser() {} |
4 | @EqualsAndHashCode(of = {"id"}) | Generates equals and hashcode implementation such as 2 User objects with same "id" attribute are equal. |
5 | @ToString(of = {"id", "email"}) | Generates toString method returning id and email public String toString() { return"User(id=" + getId() + ", email=" + getEmail() + ")"; } |
In addition to above, lombok provides few more annotations like
@AllArgsConstructor
to generate constructor using all the attributes
2.2 Not null checks :
Often it is required that while setting an attribute, we have to perform not null checks. Lombok makes it easy to set these rules :
Here we have explicitly annotated attributes id, email and mobile with @NonNull.
If take on a field, any posted method indicating a value to this field will also generate these null test. Thus here setters of these fields have NULL checks before the value is indicated to them.
The resultant decompiled setters are:
Thus null checks are applied on the setters of mobile, id and email while other setters are normal.
2.3 Further reducing Annotations
Lombok provides us further mechanism to reduce the annotations.
A convenience annotation @Data is further provided.
This produces getters for all fields, a useful to String method, and hash Code and equals expectation that check all non-transient fields.
This also generate setters for all non-final fields, as well as a constructor.
@Data annotation is equivalent to {@Getter @Setter @RequiredArgsConstructor @ToString @EqualsAndHashCode}
3. Lombok Support for Builder Pattern
Builder design pattern is mostly employed when:
- Class has many attributes. In this case creating an object via default constructor and explicitly setting required attributes means object creation becomes lengthy.
- Even if we choose parameterized constructor, the number of parameters in method are quite huge. Also this forces us to send NULL for parameters which are not needed.
- Same are the issues with object is constructed via Factory.
Builder pattern solves the above issues by providing a way to build the object step-by-step and provide a method (build) that will actually return the final Object which will be consistent.
The builder for our above User class is configured as :
The resultant User object can be created using builder as
Maintaining this builder can be a maintenance hazard, when the fields are added/removed in main User class.
Lombok provides @Builder annotation which does the same thing for us.
The code to use the builder is :
4. Cleanup Resources
In java when we deal with resources like Files, Input/Output streams, then its a bad practice to let the resources open. Ideal way is to cleanup the resources in finally block before exiting the function.
With lombok’s @Cleanup annotation, we no longer have to remember to cleanup the resources.
Consider the following code :
Here compiler generates warning “Resource leak :sc is never closed”.
On annotating the Scanner sc with @Cleanup, we never have to worry about cleaning/closing it.
5. Handling checked Exceptions
We often come across API methods which throw checked exceptions like SQLException, IOException. Here our methods are forced to throw them too. An alternate way is swallowing these checked exceptions, logging them and wrapping them in runtime/unchecked exceptions, so that compiler doesn’t complain.
My @springboot + @RabbitMQ + @project_lombok + @java 8 sample has been updated to Spring Boot 1.5.13 and finally I added hints in the readme file https://t.co/AYj9uSvVU2 pic.twitter.com/A6xbA771rR
— Maciej Walkowiak (@maciejwalkowiak) May 16, 2018
Lombok provides the @SneakyThrows annotation.
This can be placed on a method to essentially “swallow” the checked exceptions, allowing you to omit the try-catch block completely.
Consider the following code sample
Here the FileInputStream throws FileNotFoundException which is checked exception and hence compiler will continue to give error until method declares it. This is done as follows :
Further if we don't want to change the method signature, then we need to write the logic to swallow the exception on our own.
With @SneakyThrows, the lombok implements exception swallowing logic.
6. Logging
In java applications, we employ various logging frameworks like Log4j, Logback for efficient logging. In addition to it, we use Logging facade like Slf4j so that underlying logging implementation can be changed without affecting the code.
For Slf4J the typical way to declare and implement the logging is :
Lombok takes care of the logger declaration if you place the @Log annotation.
Thus we can directly start using logger without defining it.
Lombok provides annotations for different logging frameworks -
- @Log4j - Log4j framework
- @JBossLog - JBoss logger
- @CommonsLog - apache commons logger
7. Conclusion:
Thus we have explored major feature offerings of Project Lombok to reduce verbosity of the Java software development company
A full list of its features can be found at https://projectlombok.org/features/all