How to Use Lombok with Java
2 CommentsLast 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!
Jay
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.”
jose paz
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.