How to Use Lombok with Java

How to Use Lombok with Java

2 Comments

Last Updated on October 22, 2024 by jt

Project Lombok is a Java library tool that generates code for minimizing boilerplate code. The library replaces boilerplate code with easy-to-use annotations.

For example, by adding a couple of annotations, you can get rid of a lot of cerimonial code, such as getters and setters methods, constructors, hashcode, equals, and toString methods, and so on.

In this post we will look at the following Lombok constructs:

  • var and val
  • @Getter, @Setter
  • @NoArgsConstructor, @AllArgsConstructor
  • @Data
  • @NotNull

Why Should you use Project Lombok for Java Develoment?

  • Simplified Code Structure – Lombok reduces boilerplate code, which leads to cleaner code. You can focus on functionality over ceremony.
  • Enhance Code Clarity – With unnecessary code clutter minimized, the actual intent behind your Java applications becomes more visible. This clarity enables developers to grasp the logic and flow of the program at a glance, making collaboration and maintenance much more efficient.
  • Improved Development Efficiency – Lombok frees up valuable time for developers to concentrate on business logic and more complex tasks. This not only speeds up the development cycle but also contributes to higher productivity and faster project delivery.
  • Streamlined Refactoring –  When changes are made to field names, the need to manually update all associated methods like getters and setters is eliminated. This reduces the potential for errors and significantly cuts down on the time required to maintain and update code.

Lombok Dependency

Maven Dependency

To use Lombok in your project, add the lombok dependency to the Maven POM, like this.

<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
   <version>1.18.34</version>
   <scope>provided</scope>
</dependency>

Note: If you’re using a Spring Boot POM, Project Lombok is a curated dependency. Thus, you can omit the version (which will then be inherited from the Spring Boot parent POM).

Gradle Dependency

compileOnly group: 'org.projectlombok', name: 'lombok', version: '1.18.34'

val and var

You can use val as the type of a local variable instead of writing the actual type. Lombok infers the type from the initializer expression. Lombok will also mark the local variable as final.

var works exactly like val, except the local variable is not marked as final.

The code for using val and var is this.

import java.math.BigDecimal;
import java.util.ArrayList;

import lombok.val;

public class ValAndVarUserDemo {
  public String valCheck() {
    /*
    val makes local final variable (inside method)
    Trying to assign a value will result in
    Error: java: cannot assign a value to final variable userName
    */
    val userName = "Hello World";
    System.out.println(userName.getClass());
    return userName.toLowerCase();
  }

  public Object varCheck() {
    /*
    var makes local variable (inside method).
    Same as var but is not marked final
    */
    var money = new BigDecimal(53.00);
    System.out.println(money.getClass());
    money = new BigDecimal(80.00);
    return money;
  }
}

The decompiled ValAndVarUserDemo.class is this.

Note: If you are using IntelliJ, double-click the class file inside the target folder to view the decompiled class.

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package guru.springframework.domain.valandvar;

import java.math.BigDecimal;

public class ValAndVarUserDemo {
  public ValAndVarUserDemo() {
  }

  public String valCheck() {
    String userName = "Hello World";
    System.out.println("Hello World".getClass());
    return "Hello World".toLowerCase();
  }

  public Object varCheck() {
    BigDecimal money = new BigDecimal(53.0D);
    System.out.println(money.getClass());
    money = new BigDecimal(80.0D);
    return money;
  }
}

The code for testing the ValAndVarUserDemo class is this.

package guru.springframework.domain.valandvar;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.math.BigDecimal;

import static org.junit.Assert.*;

public class ValAndVarUserDemoTest {
  private ValAndVarUserDemo valAndVarUserDemo;

  @Before
  public void setUp() throws Exception {
    valAndVarUserDemo = new ValAndVarUserDemo();
  }

  @After
  public void tearDown() throws Exception {
    valAndVarUserDemo = null;
  }

  @Test
  public void testValUsage() {
    assertEquals("hello world", valAndVarUserDemo.valCheck());
  }

  @Test
  public void testVarUsage() {
    assertEquals(new BigDecimal(80), valAndVarUserDemo.varCheck());
  }
}

@Getter and @Setter

You can use the @Getter and @Setter annotations at both the field or class level to generate getters and setters for private fields.

When you use them at the field level, Lombok generates getters and setters only for the decorated fields.

The code to use the @Getter and @Setter annotations at the field level is this.

import lombok.Getter;
import lombok.Setter;

public class FieldLevelGetterSetterDemo {

  private int userId;
  @Getter 
  @Setter
  private String userName;
  
  @Getter
  private int userAge;
  
  public FieldLevelGetterSetterDemo(int userAge){
    this.userAge=userAge;
  }
}

This code annotates userName with @Getter and @Setter. The code also annotates userAge with @Getter.

The decompiled FieldLevelGetterSetterDemo.class is this.

public class FieldLevelGetterSetterDemo {
  private int userId;
  private String userName;
  private int userAge;

  public FieldLevelGetterSetterDemo(int userAge) {
    this.userAge = userAge;
  }

  public String getUserName() {
    return this.userName;
  }

  public void setUserName(final String userName) {
    this.userName = userName;
  }

  public int getUserAge() {
    return this.userAge;
  }
}

The preceding code shows the getUserName() and setUserName() methods that Lombok generates for the userName field. Also note that Lombok generates a single getUserAge() method for the userAge field.

When you use the @Getter and @Setter annotations at the class level, Lombok generates getter and setter methods for all the fields.

import lombok.*;

/*
@Getter and @Setter annotations for getter and setter methods
*/
@Getter
@Setter
public class GetterSetterUserDemo {
  private int userId;
  private String userName;
  private int userAge;
}

The decompiled GetterSetterUserDemo.class is this.

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package guru.springframework.domain.gettersetter;

public class GetterSetterUserDemo {
  private int userId;
  private String userName;
  private int userAge;

  public GetterSetterUserDemo() {
  }

  public int getUserId() {
    return this.userId;
  }

  public String getUserName() {
    return this.userName;
  }

  public int getUserAge() {
    return this.userAge;
  }

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

  public void setUserName(final String userName) {
    this.userName = userName;
  }

  public void setUserAge(final int userAge) {
    this.userAge = userAge;
  }
}

As you can see, Lombok generates getter and setter methods for all the fields.

@NoArgsConstructor and @AllArgsConstructor

You can use the @NoArgsConstructor annotation to generate the default constructor that takes no arguments. To generate a constructor with arguments for all the field, use the @AllArgsConstructor annotation.

The code for demonstrating @NoArgsConstructor and @AllArgsConstructor annotations is this.

import lombok.*;

/*
@NoArgsConstructor annotation for generating a constructor with no parameters
*/
@NoArgsConstructor
/*
@AllArgsConstructor annotation for generating a constructor
with 1 parameter for each field
*/
@AllArgsConstructor
public class ConstructorUserDemo {
  private int userId;
  private String userName;
  private int userAge;
}

In the preceding code, we have annotated the class with @NoArgsConstructor and @AllArgsConstructor. Lombok will generate two constructors in the .class file. One without parameters and the other with a parameter for each field.

The decompiled ConstructorUserDemo.class is this.

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package guru.springframework.domain.constructor;

public class ConstructorUserDemo {
  private int userId;
  private String userName;
  private int userAge;

  public ConstructorUserDemo() {
  }

  public ConstructorUserDemo(final int userId, final String userName, final int userAge) {
    this.userId = userId;
    this.userName = userName;
    this.userAge = userAge;
  }
}

@Data

@Data is a convenient annotation that combines the features of the following annotations:

  • @ToString
  • @EqualsAndHashCode
  • @Getter
  • @Setter
  • @RequiredArgsConstructor

This code demonstrates the @Data annotation.

import lombok.Builder;
import lombok.Data;

@Data
public class DataUserDemo {
  private int userId;
  private String userName;
  private int userAge;
}

The decompiled DataUserDemo.class is this.

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package guru.springframework.domain.data;

public class DataUserDemo {
  private int userId;
  private String userName;
  private int userAge;

  public DataUserDemo() {
  }

  public int getUserId() {
    return this.userId;
  }

  public String getUserName() {
    return this.userName;
  }

  public int getUserAge() {
    return this.userAge;
  }

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

  public void setUserName(final String userName) {
    this.userName = userName;
  }

  public void setUserAge(final int userAge) {
    this.userAge = userAge;
  }

  public boolean equals(final Object o) {
    if (o == this) {
      return true;
    } else if (!(o instanceof DataUserDemo)) {
      return false;
    } else {
      DataUserDemo other = (DataUserDemo)o;
      if (!other.canEqual(this)) {
        return false;
      } else if (this.getUserId() != other.getUserId()) {
        return false;
      } else {
        Object this$userName = this.getUserName();
        Object other$userName = other.getUserName();
        if (this$userName == null) {
          if (other$userName == null) {
            return this.getUserAge() == other.getUserAge();
          }
        } else if (this$userName.equals(other$userName)) {
          return this.getUserAge() == other.getUserAge();
        }

      return false;
    }
  }
}

  protected boolean canEqual(final Object other) {
    return other instanceof DataUserDemo;
  }

  public int hashCode() {
    int PRIME = true;
    int result = 1;
    int result = result * 59 + this.getUserId();
    Object $userName = this.getUserName();
    result = result * 59 + ($userName == null ? 43 : $userName.hashCode());
    result = result * 59 + this.getUserAge();
    return result;
  }

  public String toString() {
    int var10000 = this.getUserId();
    return "DataUserDemo(userId=" + var10000 + ", userName=" + this.getUserName() + ", userAge=" + this.getUserAge() + 
    ")";
  }
}

In the preceding code, Lombok generated getters for all fields, setters for all non-final fields, toString, equals and hashCode implementation and a constructor.

@NonNull

Lombok generates a null check statement if we annotate the parameters of a method or a constructor with @NonNull.

This code shows the usage of @NonNull.

import lombok.AllArgsConstructor;
import lombok.NonNull;

public class NonNullUserDemo {
  private int userId;
  private String userName;
  private int userAge;

  /*
  @NonNull generate a null-check statement
  */
  public NonNullUserDemo(int userId, @NonNull String userName, int userAge) {
    this.userId = userId;
    this.userName = userName;
    this.userAge = userAge;
  }
}

The preceding code annotates the userName parameter as @NonNull. Lombok will generate code to check for userName and throw NullPointerException if userName is null.

The decompiled NonNullUserDemo.class is this.

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package guru.springframework.domain.nonnull;

import lombok.NonNull;

public class NonNullUserDemo {
  private int userId;
  private String userName;
  private int userAge;

  public NonNullUserDemo(int userId, @NonNull String userName, int userAge) {
    if (userName == null) {
      throw new NullPointerException("userName is marked non-null but is null");
    } else {
      this.userId = userId;
      this.userName = userName;
      this.userAge = userAge;
    }
  }
}

Summary

Lombok is a convenient tool that all Java developers should have in their toolkit. It not only makes you code clutter-free, but also saves a significant amount of development time.

When using Lombok for the first time, you might stumble on how to configure it in your IDE. In IntelliJ, you need to have the IntelliJ Lombok plugin. You also need to enable annotation processing. In IntelliJ, go to File->Settings->Build, Execution,Deployment->Compiler->Annotation Processors. Select the Enable annotation processing checkbox.

The source code for this post can be found here on GitHub.

Spring Framework 6: Beginner to Guru

Checkout my best selling course on Spring Framework 6. This is the most comprehensive course you will find on Udemy. All things Spring!

About SFG Contributor

Staff writer account for Spring Framework Guru

    You May Also Like

    2 comments on “How to Use Lombok with Java

    1. July 26, 2020 at 11:18 am

      I’ve been using Lombok from last 5- months. However, I didn’t notice any issues. could you please elaborate more on – ” Lombok internally uses the annotation processor API as the entry point. This API only allows the creation of new files during the compilation and not the modification of the existing files.”

      Reply
    2. September 22, 2020 at 4:01 pm

      very cool tutorial, I have it now running and I believe this will save me millions of keystrokes. I believe my classes are very much readable. Loved the @Slf4j annotation found on their website.

      Reply

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    This site uses Akismet to reduce spam. Learn how your comment data is processed.