7.8. Reducing Boilerplate Code (Lombok)

7.8.1. Lombok

Lombok is a library used for reducing the boilerplate code from Java source code.

Boilerplate code is a typical source code that cannot be omitted by language specification. Basically Boilerplate code does not have a specific logic hence it becomes redundant code in implementation.

Following are the typical Boilerplate source code in Java language.

  • getter / setter methods for accessing the member variables
  • equals/hashCode methods
  • toString methods
  • Constructors
  • Closing process of resources (input and output stream, etc.)
  • Generation of logger instance

In Lombok, boilerplate code gets generated at the time of compilation thereby providing a mechanism to remove redundant code from the source code developed by the developer.

Tip

For removing the Boilerplate code, the language specification for closing the resources (input and output stream, etc.) are improved by newly added [try-with-resources] statement in Java SE7.

Java language itself is improving in every version-up for removing the redundant code. Lambda support in Java SE8 is also called as typical language specification improvement.


7.8.2. Efficiency of Lombok

Below, JavaBean source code is created using Lombok.

package com.example.domain.model;

@lombok.Data
public class User {

    private String userId;
    private String password;

}

In Lombok, required methods for JavaBean gets created by only assigning @lombok.Data annotation at class level.

Generated Class Structure by Lombok

Class structure generated by Lombok

By only assigning @Data annotation of Lombok, it is possible to obtain the same effect as classes generated by (the source code output using the auto-generation function of Eclipse) which has about 60 lines of code given below instead of 10 lines of code.

package com.example.domain.model;

public class User {

    private String userId;
    private String password;

    public User() {
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((password == null) ? 0 : password.hashCode());
        result = prime * result + ((userId == null) ? 0 : userId.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        User other = (User) obj;
        if (password == null) {
            if (other.password != null)
                return false;
        } else if (!password.equals(other.password))
            return false;
        if (userId == null) {
            if (other.userId != null)
                return false;
        } else if (!userId.equals(other.userId))
            return false;
        return true;
    }

    @Override
    public String toString() {
        return "User [userId=" + userId + ", password=" + password + "]";
    }

}

7.8.3. Lombok setup

7.8.3.1. Inclusion of dependent library

In order to use a class that is offered by Lombok, add Lombok as dependency library.

<!-- (1) -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <scope>provided</scope> <!-- (2) -->
</dependency>
Sr.No Description
(1)
Add Lombok dependent library in the Lombok targeted project’s pom.xml .
(2)
Since Lombok library is not required at the time of application execution, appropriate scope is provided.

Note

In the above configuration example, it is prerequisite that the version of dependent library is to be managed by the parent project. Therefore, <version> element is not specified.


7.8.3.2. IDE Integration

If you want to use Lombok on IDE, it is necessary to install the Lombok to IDE in order to work with compile (build) function provided by the IDE.

In this guideline, introduced how to install Lombok to Spring Tool Suite (Later referred as the “STS”). However installation methods are different depending on IDE henceforth refer this page in case you want to use IDE besides STS.


7.8.3.2.1. Download Lombok

Download the jar file of Lombok.

The jar file of Lombok,

  • Download page of Lombok
  • Local repository of Maven can retrieve from (Normally, $HOME/.m2/repository/org/projectlombok/lombok/<version>/lombok-<version>.jar)

7.8.3.2.2. Lombok Installation

Launch the installer by running (double-click) the downloaded Lombok jar file.

Lombok Installer

Lombok Installer

After selecting the targeted STS, follow installation process by pressing the “Install / Update” button. The installer will automatically detect the location of supported IDE However if cannot auto detected, it is necessary to specify the IDE by pressing the “Specify location …”

Lombok Install Successful

Successful installation dialog

Once Lombok installation completes, it is possible to start development using Lombok on STS after booting (Or re-booting) STS.


7.8.4. How to use Lombok

From here, the specific use of Lombok is described.

If Lombok is used first time, it is recommendation to watch Lombok [Demo Video]. The length of Demo Video is less than 4 minutes and described the most basic usage.


7.8.4.1. Lombok Annotations

Typical annotations provided by Lombok are introduced below.

For Detailed usage of each annotation as well as annotation that have not been explained in this guideline,

please refer,


Sr.No Annotation Description
@lombok.Getter

Annotation for generating getter method.

If annotation is specified at class level, it is possible to generate getter methods for all respective fields.

@lombok.Setter

Annotation for generating setter method.

If annotation is specified at class level, it is possible to generate setter methods for all non-final respective fields.

@lombok.ToString Annotation for generating toString method.
@lombok.EqualsAndHashCode Annotation for generating equals and hashCode method.
@lombok.RequiredArgsConstructor

Annotation for generating constructor with the required arguments for those fields (final field etc) where initialization required.

If all fields are optional fields, the default constructor (without argument constructor) is generated.

@lombok.AllArgsConstructor Annotation for generating constructor having all fields in arguments.
@lombok.NoArgsConstructor Annotation for generating default constructor.
@lombok.Data

Short cut annotation for @Getter, @Setter, @ToString, @EqualsAndHashCode, @RequiredArgsConstructor.

If @Data annotation is specified, it has the same meaning as specification of all above five annotations.

@lombok.extern.slf4j.Slf4j Annotation for generating logger instance of SLF4J.

7.8.4.2. Creation of JavaBean

If an application is built the way that this guideline is recommended,

  • Form class
  • Resource class(REST API configured)
  • Entity class
  • DTO class

it is necessary to create a JavaBean for above classes.

Below is the example of creating a JavaBean.

package com.example.domain.model;

import lombok.Data;

@Data // (1)
public class User {

    private String userId;
    private String password;

}
Sr.No Description
(1)

By assigning a @Data annotation at class level,

  • getter/setter method
  • equals/ hashCode method
  • toString method
  • default constructor

are created.


7.8.4.2.1. How to exclude specific field from toString

At the time of converting the state of object into a string,

  • Field that holds an object of cross reference relationship
  • Field that holds sensitive information such as personal information and password

etc are required to exclude from the scope of string conversion. If these fields are not excluded from the string conversion,

  • The StackOverflowError and OutOfMemoryError occurs due to circular reference
  • There is a possibility of leakage the personal information due to use of converted string

Henceforth it is necessary to take an attention.

Warning

If @Data or @ToString annotation is used at the Entity class of JPA, it is necessary to keep in mind that it tends to the circular reference.


How to exclude a specific field from string conversions are indicated below.

package com.example.domain.model;

import lombok.Data;
import lombok.ToString;

@Data
@ToString(exclude = "password") // (1)
public class User {

    private String userId;
    private String password;

}
Sr.No Description
(1)

Specify the @ToString annotation to the class level and list the name of fields that you want to exclude into exclude attribute.

If you call toString method of the class that is generated from the source code of above example,

  • User(userId=U00001)

is converted to the string.


7.8.4.2.2. How to exclude specific field from equals and hashCode

If equals method and hashCode method generated using Lombok annotation, field that holds an object of cross reference relationship needs to be removed.

If methods are generated without excluding these fields, the StackOverflowError and OutOfMemoryError occurs due to circular reference henceforth it is necessary to take an attention.

Warning

If @Data annotation, @Value annotation, @EqualsAndHash annotation is used at the Entity class of JPA, it is necessary to keep in mind that it tends to the circular reference.


How to exclude a specific field is indicated below.

package com.example.domain.model;

import java.util.List;

import lombok.Data;

@Data
public class Order {

    private String orderId;
    private List<OrderLine> orderLines;

}
package com.example.domain.model;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;

@Data
@ToString(exclude = "order")
@EqualsAndHashCode(exclude = "order") // (1)
public class OrderLine {

    private Order order;
    private String itemCode;
    private int quantity;

}
Sr.No Description
(1)
Specify the @EqualsAndHashCode annotation to the class level and list the name of fields that you want to exclude into exclude attribute.

Tip

Instead of specifying the field to be excluded, it is also possible to specify to use only specific fields.

@Data
@ToString(exclude = "order")
@EqualsAndHashCode(of = "itemCode") // (2)
public class OrderLine {

    private final Order order;
    private final String itemCode;
    private final int quantity;

}
Sr.No Description
(2)

In case of using only specific fields, specify the name of fields that you want to include into of attribute of @EqualsAndHashCode annotation.

In the above example, equals method and hashCode method get generated by referring only itemCode field.


7.8.4.2.3. How to generate constructor for field initialization

If you want to create an instance of JavaBean from the implementation code of application, it is more convenient that having constructor where initial value of the field can be passed as an argument, to eliminate the redundant code.

If you create an instance using the default constructor, the code would be like below.

public void login(String userId, String password) {
    User user = new User();
    user.setUserId(userId);
    user.setPassword(password);
    // ...
}

How to generate a constructor that specifies the initial values of the field are indicated below.

package com.example.domain.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Data
@AllArgsConstructor // (1)
@NoArgsConstructor  // (2)
@ToString(exclude = "password")
public class User {

    private String userId;
    private String password;

}
public void login(String userId, String password) {
    User user = new User(userId, password); // (3)
    // ...
}
Sr.No Description
(1)
Specify the @AllArgsConstructor annotation to the class level, to generate a constructor that takes the initial values of all fields as an argument.
(2)

Specify the @NoArgsConstructor annotation to the class level, to generate a default constructor.

It is necessary to generate a default constructor if going to be used as JavaBean.

(3)

Create an instance of JavaBean by calling the constructor that having initial values of the field.

If default constructor is used, instance can be generated in one step instead of 3 steps.

Tip

If you want to create above User class as Immutable class instead of JavaBean, it is preferable to use @lombok.Value annotation. Please refer Lombok reference for @Value annotation.


7.8.4.3. Creating logger instance

If it is necessary to generate a logger instance for output a debug log and application log, it is preferable to use annotations for creating a logger instance.

If you want to create a logger instance without using Lombok annotations, below could be code.

package com.example.domain.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

@Service
public class AuthenticationService {

    private static final Logger log = LoggerFactory.getLogger(AuthenticationService.class);

    public void login(String userId, String password) {
        log.info("{} had tried login.", userId);
        // ...
    }

}

How to create a logger instance using Lombok annotation is described below.

package com.example.domain.service;

import org.springframework.stereotype.Service;

import lombok.extern.slf4j.Slf4j;

@Slf4j // (1)
@Service
public class AuthenticationService {

    public void login(String userId, String password) {
        log.info("{} had tried login.", userId); // (2)
        // ...
    }

}
Sr.No Description
(1)

Generate SLF4J logger instance by specifying @Slf4j annotation at the class level.

In this guideline, it is prerequisite to output a log using org.slf4j.Logger of SLF4J.

By default, FQCN class that granted the annotation (In above example com.example.domain.service.LoginService) is used as the logger name and, logger instance corresponding to the logger name is set to field called log.

(2)

Output the log by calling the method of SLF4J logger instance that has been generated by Lombok.

In above example,

  • 11:29:45.838 [main] INFO  c.e.d.service.AuthenticationService - U00001 had tried login.

will be output.

Tip

If you want to change the logger name that is used by default, specify optional logger name in the topic attribute of @Slf4j annotation.