Project Lombok is a Java library that helps to reduce the boilerplate code. In this tutorial, you will learn how Lombok helps us to create getter, setters, toString, constructor, equals, hashCode methods, etc automatically. This article mainly includes information about Project Lombok and it’s annotations with examples.

How does Lombok work?

Lombok has a collection of annotations which are being used in our code (.java file). These annotations are processed during the compile-time and appropriate code expansion would take place based on the annotation used (code gets generated in the .class file). Point to be noted that Lombok only does the code reduction in view time, after compiling the byte code is injected with all the boilerplate. This helps to keep the codebase small, clean and easy to read and maintain. We will cover these topics in details in the below part of this article.

Steps to use Project Lombok in your project

The steps to use project Lombok is as simple as below.

  1. Add the lombok.jar dependency in the build tool (Maven, Gradle etc).
  2. Install Lombo plugin and configure your IDE (Eclipse, IntelliJ etc) to work with it.
  3. Add the appropriate annotations in your code. Rest everything will be taken care of automatically by your IDE.

1. Add Lombok dependency in Maven

Adding the Lombok in a Maven project is simple, just add the below dependency in your maven project pom.xml file. If you are new to the Java ecosystem, check out the tutorial on Maven.

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

2. Integrate Lombok with IDE

Install in Eclipse

Run the lombok.jar with this command: java -jar Lombok.jar which downloadable from Lombok official site, or from your maven repository. It starts the eclipse installer and will find eclipse to install it.

Lombok-Installation
Lombok-Installationsuccessful

IntelliJ IDEA

  1. Go to File > Settings > Plugins
  2. Click on Browse repositories...
  3. Search for Lombok Plugin
  4. Click on Install plugin
  5. Restart IntelliJ IDEA

NOTE: You may need to restart your IDE after installing Lombok, As it may possible that changes didn’t reflect properly.

List of annotations in Lombok

1. @Getter and @Setter annotations

You can use @Getter and @Setter annotations to create the getters and setters for POJO classes. This helps in elimination of boiler-plate and unnecessary writing the code. These annotations work on two levels, Class level declaration and Field level declaration. Please go through the example below to understand this further.

Class Level annotations.

package jstobigdata.lombokexample;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
class GetSetWithVariables {

    private String product;
    private String description;
    private int price;

}

Field Level Annotations

class GetSetWithFields {

    @Getter
    @Setter
    private String employeeName;
    @Getter
    @Setter
    private String employeeDept;
    @Getter
    @Setter
    private int employeeSalary;
}

As you can see below, getters/setters can be accessed as usual.

public class GetterSetterExample {
    public static void main(String[] args) {
        // setting values for class level annotations.
        GetSetWithVariables classlevel = new GetSetWithVariables();
        classlevel.setProduct("");
        classlevel.setDescription("");
        classlevel.setPrice(100);

        // setting values for field level annotations
        GetSetWithFields fieldLevel = new GetSetWithFields();
        fieldLevel.setEmployeeName("");
        fieldLevel.setEmployeeDept("");
        fieldLevel.setEmployeeSalary(50000);

    }

}

2. Constructors annotations – @NoArgsConstructor, @RequiredArgsConstructor, and @AllArgsConstructor

@NoArgsConstructor annotation

This generates a constructor with no parameters. If this is not possible (because of final fields), a compilation error will result in instead. Unless @NoArgsConstructor(force = true) is used, then all final fields are initialized with 0 / false / null. For fields with constraints, such as @NonNull fields, no check is generated, so be aware that these constraints will generally not be fulfilled until those fields are properly initialized later.

package lombokexample;

import lombok.NoArgsConstructor;

//Code by using Lombok noconstructor annotation.
@NoArgsConstructor
public class NoArgsConstructor {

    private int constructorvalue;
    private String constructorname;
    private int uniqueconstructor;

    }

//The above code will generate the below code after compilation.
    Public Class NoArgsConstructor
    {

    private int constructorvalue;
    private String constructorname;
    private int uniqueconstructor;

    public noargsConstructor() {
    }
}

@RequiredArgsConstructor annotation

This annotation works by assigning it to a class. It generates a constructor with arguments to set the value to a required member (final member).

package lombokexample;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public class RequiredConstructor {

    private final int constructorvalue;
    private String constructorname;
    private int uniqueconstructor;

    }

//The above code will generate the bvelow code after comipation.
    Public Class RequiredConstructor
    {

    private final int constructorvalue;
    private String constructorname;
    private int uniqueconstructor;

    public RequiredConstructor(final int constructorvalue) {
    }
}

@AllArgsConstructor annotation

This generates a constructor with 1 parameter for each field in your class. Only fields marked with @NonNull result in null checks on those parameters.

package lombokexample;
import lombok.AllArgsConstructor;

@AllArgsConstructor

public class AllArgsConstructor {

	private int constructorvalue;
	private String constructorname;
	private int uniqueconstructor;

	}

//The above code will generate the bvelow code after comipation.
	Public Class AllArgsConstructor
	{
	
	private final int constructorvalue;
	private String constructorname;
	private int uniqueconstructor;

	public AllArgsConstructor(int constructorvalue,String constructorname,int uniqueconstructor) {
	this.constructorvalue = constructorvalue;
	this.constructorname = constructorname;
	this.uniqueconstructor = uniqueconstructor;
	
	}
}

3. val and var datatypes

With val and var, you can write Java code without specifying the local variable datatype. Use Val as the type of any final local variable declaration and the type will be inferred from the initializing expression itself. Var works exactly as same as Val except the local variable is not marked as final. Let’s look at the example below:

package lombokexample;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;

import lombok.val;
import lombok.var;

public class ValAndVar {

    public static void main(String[] args) {

        // First Example with val to have final local variables.

        val set = new HashSet<String>();
        // After compilation above will be converted into:
        // final HashSet<String> set = new HashSet<String>();
        set.add("lombok");
        set.add("example");

        // Second Example with val.
        val intvalue = 10; // After compilation = final int intvalue = 10;
        val map = new HashMap<Integer, String>();
        // After compilation above will be converted into:
        // final HashMap<INteger,String> map = new HashMap<Integer, String>();
        map.put(1, "lombok");
        map.put(intvalue, "Exammple");

        /* Let us look the same piece of code wuth VAR. */

        var varset = new HashSet<String>();
        // After compilation above will be converted into:
        // final HashSet<String> varset = new HashSet<String>();
        varset.add("lombok");
        varset.add("example");

        // Second Example with val.
        var varintvalue = 10; // After compilation int varintvalue = 10;
        var varmap = new HashMap<Integer, String>();
        // After compilation above will be converted into:
        // HashMap<INteger,String> varmap = new HashMap<Integer, String>();
        varmap.put(1, "lombok");
        varmap.put(varintvalue, "Exammple");

    }
}

4. @NonNull annotation

@NonNullis used on the parameter of a method or constructor to have Lombok generate a null-check statement for you. On the parameter Lombok will insert a null-check at the start of the method/constructor’s body, throwing a NullPointerException with the parameter’s name as the message. On the field, any generated method assigning a value to this field will also produce these null-checks.

package lombokexample;

import lombok.NonNull;

public class NonNullAnnotation {

    public static void main(String[] args) {
        boolean flag = nonNullExpFunction(null); // passing the null value for failure condition
    }

    static boolean nonNullExpFunction(@NonNull String value) {
        if (value == null) {
            throw new NullPointerException("value is marked @NonNull but is null");
        } else
            return true;
    }

}

5. @ToString annotation

This is used when you want the toString method in the class, the Lombok’s @ToString generates a toString method automatically in which all the non-static fields are implemented by default. You can also include or exclude members in the implementation. Let’s understand this with the code examples:

package lombokexample;

import lombok.ToString;

@ToString
public class ToStringAnnotation {

    private String Student;
    private String roll;

    /****
     * Compile time gneration of toString () method
     * 
     * public String toString() { return "tostringannotation [Student=" + Student +
     * ", roll=" + roll + "]"; }
     * 
     */

}

Include with @ToString.Include

Most of the developers generally use this annotation along with property onlyExplicitlyIncluded = true, you can include the specific fields in the implementation of the toString method with the help of this annotation. We include our members generally on three levels – class, method and field respectively.

package lombokexample;

import lombok.ToString;

@ToString(of = { "Student", "address" })
public class ToStringIncludeClassLevel {

	private String Student;
	private String grade;
	private int roll;
	private String address;

	/***
	 * 
	 * //toString method will only consist the two variables as defined in the class
	 * level @toString annotation. public String toString() { return
	 * "tostringincludeclasslevel [Student=" + Student + ", address=" + address +
	 * "]"; }
	 */

}
package lombokexample;

import lombok.ToString;

@ToString(onlyExplicitlyIncluded = true)
public class ToStirnIncludeMethodLevel {

    private String Student;
    private String grade;
    private int roll;
    private String address;

    @ToString.Include
    public String demoMethod() {
        return "String is returned";
    }

    /***
     * 
     * //toString method will only consist the method public String toString() {
     * return "tostirnincludemethodlevel [demoMethod()="+ demoMethod() + "]"; }
     */

}
package lombokexample;

import lombok.ToString;

@ToString(onlyExplicitlyIncluded = true)
public class ToStringIncludeFieldLevel {

    private String Student;
    @ToString.Include
    private String grade;
    private int roll;
    @ToString.Include
    private String address;

    /***
     * toString method will only consist the two variables as defined in the class
     * public String toString() { return "tostringincludefieldlevel [grade=" + grade
     * + ", address=" + address + "]"; }
     * 
     */
}

Exclude with @ToString.Include

Just like above examples, you can also exclude the members by using @ToString.Exclude.

6. @EqualsAndHashcode annotation

Similar to @toString, @EqualsAndHashCode annotation generates the equals() and hashcode() methods. By default, it uses all the non-static and non-transient fields, by using exclude parameter you can exclude the specified field or with the ‘of’ parameter you can include a field.

package lombokexample;

import lombok.EqualsAndHashCode;

@EqualsAndHashCode
public class EqualAndHashAnnotation {

    private String Student;
    private String grade;
    private int roll;
    private String address;

    /*
     * Compile time generated equals() and hashcode() methods.
     * 
     * @Override public boolean equals(Object obj) { if (this == obj) return true;
     * if (obj == null) return false; if (getClass() != obj.getClass()) return
     * false; equalandhashannotation other = (equalandhashannotation) obj; if
     * (Student == null) { if (other.Student != null) return false; } else if
     * (!Student.equals(other.Student)) return false; if (address == null) { if
     * (other.address != null) return false; } else if
     * (!address.equals(other.address)) return false; if (grade == null) { if
     * (other.grade != null) return false; } else if (!grade.equals(other.grade))
     * return false; if (roll != other.roll) return false; return true; }
     * 
     * @Override public int hashCode() { final int prime = 31; int result = 1;
     * result = prime * result + ((Student == null) ? 0 : Student.hashCode());
     * result = prime * result + ((address == null) ? 0 : address.hashCode());
     * result = prime * result + ((grade == null) ? 0 : grade.hashCode()); result =
     * prime * result + roll; return result; }
     */
}

7. @Data annotation

Data is the shortcut and very useful annotation which works as a combined package for @ToString@EqualsAndHashCode@Getter / @Setter, and @RequiredArgsConstructor. Data annotations generally used for POJO classes which reduces the boilerplate code in an impressive way.

package lombokexample;

import lombok.ToString;

public class DataAnnotation {

    private String Student;
    private String grade;
    private int roll;
    private String address;

    // Constructor using the fields

    public DataAnnotation(String student, String grade, int roll, String address) {
        super();
        Student = student;
        this.grade = grade;
        this.roll = roll;
        this.address = address;
    }

    // All the Getters and Setters.

    public String getStudent() {
        return Student;
    }

    public void setStudent(String student) {
        Student = student;
    }

    public String getGrade() {
        return grade;
    }

    public void setGrade(String grade) {
        this.grade = grade;
    }

    public int getRoll() {
        return roll;
    }

    public void setRoll(int roll) {
        this.roll = roll;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    // to String Method:
    @Override
    public String toString() {
        return "dataannotation [Student=" + Student + ", grade=" + grade + ", roll=" + roll + ", address=" + address
                + "]";
    }

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

    // equals() and hashcode():

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        DataAnnotation other = (DataAnnotation) obj;
        if (Student == null) {
            if (other.Student != null)
                return false;
        } else if (!Student.equals(other.Student))
            return false;
        if (address == null) {
            if (other.address != null)
                return false;
        } else if (!address.equals(other.address))
            return false;
        if (grade == null) {
            if (other.grade != null)
                return false;
        } else if (!grade.equals(other.grade))
            return false;
        if (roll != other.roll)
            return false;
        return true;
    }

}

Let’s try to add @Data annotation and check how the same class looks now.

package lombokexample;

import lombok.Data;

@Data
class TestingClass {
    private String Student;
    private String grade;
    private int roll;
    private String address;
}

public class DataAnnotationLombok {
    public static void main(String[] args) {

        TestingClass obj = new TestingClass();
        // testing all the setter methods
        obj.setAddress("Demo String");
        obj.setGrade("demo String");
        obj.setRoll(1);
        obj.setStudent("Demo String");

        testingclass obj1 = new testingclass();
        // testing all the setter methods
        obj.setAddress("Demo String");
        obj.setGrade("demo String");
        obj.setRoll(1);
        obj.setStudent("Demo String");

        // testing eqqals method
        System.out.println(obj.equals(obj1));

        // testing hashcode method
        System.out.println(obj.hashCode());
        System.out.println(obj1.hashCode());

        // testing teString method
        System.out.println(obj.toString());

    }
}

8. @Value annotation

@Value works same as @data. The only difference is that @value creates immutable objects .i.e, there will be no setters, the class will be final and all the members will be private and final by default to achieve the immutability feature.

package lombokexample;

import lombok.Value;
import lombok.experimental.NonFinal;
import lombok.experimental.PackagePrivate;

@Value
@NonFinal // Overriding the default behaviour by setting the class as nonfinal
public class ValueLombokAnnotation {

    @NonFinal // making the field non final
    @PackagePrivate // making the field non-private by overriding the degault behaviour of value
                    // annotation
    private String Student;
    private String grade;
    @NonFinal
    @PackagePrivate
    private int roll;

}
package lombokexample;

import lombok.Value;

public class ValueJavaAnnotation {

    private String Student;
    private final String grade = ""; // final
    private int roll;

    // getters and setters
    public String getStudent() {
        return Student;
    }

    public void setStudent(String student) {
        Student = student;
    }

    public final String getGrade() {
        return grade;
    }

    public int getRoll() {
        return roll;
    }

    public void setRoll(int roll) {
        this.roll = roll;
    }

    // equals and hashcode method

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

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        ValueJavaAnnotation other = (ValueJavaAnnotation) obj;
        if (Student == null) {
            if (other.Student != null)
                return false;
        } else if (!Student.equals(other.Student))
            return false;
        if (grade == null) {
            if (other.grade != null)
                return false;
        } else if (!grade.equals(other.grade))
            return false;
        if (roll != other.roll)
            return false;
        return true;
    }

    // toString method
    @Override
    public String toString() {
        return "valuejavaannotatiom [Student=" + Student + ", grade=" + grade + ", roll=" + roll + "]";
    }

}

9. @Builder annotation

@Builder helps to achieve the famous builder design pattern in java. Just by the annotation, you can achieve the builder design pattern. The annotation can be used over the class, constructor or methods.

package lombokexample;

public class BuilderJava {

    // All final attributes
    private final String firstName;
    private final String lastName;
    private final int age;
    private final String phone;
    private final String address;

    private BuilderJava(Builderhelper builder) {
        this.firstName = builder.firstName;
        this.lastName = builder.lastName;
        this.age = builder.age;
        this.phone = builder.phone;
        this.address = builder.address;
    }

    // All getter, and NO setter to provde immutability
    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public int getAge() {
        return age;
    }

    public String getPhone() {
        return phone;
    }

    public String getAddress() {
        return address;
    }

    @Override
    public String toString() {
        return "User: " + this.firstName + ", " + this.lastName + ", " + this.age + ", " + this.phone + ", "
                + this.address;
    }

    public static class Builderhelper {
        private final String firstName;
        private final String lastName;
        private int age;
        private String phone;
        private String address;

        public Builderhelper(String firstName, String lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }

        public Builderhelper age(int age) {
            this.age = age;
            return this;
        }

        public Builderhelper phone(String phone) {
            this.phone = phone;
            return this;
        }

        public Builderhelper address(String address) {
            this.address = address;
            return this;
        }

        // Return the finally consrcuted User object
        public BuilderJava build() {
            BuilderJava build = new BuilderJava(this);
            return build;
        }
    }

}
package lombokexample;

public class BuilderImpl {
    public static void main(String[] args) {
        BuilderJava entry1 = new BuilderJava.Builderhelper("Lokesh", "Gupta").age(30).phone("1234567")
                .address("Fake address 1234").build();

        System.out.println(entry1);

        BuilderJava entry2 = new BuilderJava.Builderhelper("Jack", "Reacher").age(40).phone("5655")
                // no address
                .build();

        System.out.println(entry2);
    }

}
package lombokexample;

import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@Builder
@ToString
public class BuilderAnnotation {
    private final String firstName;
    private final String lastName;
    private final int age;
    private final String phone;
    private final String address;

}
package lombokexample;

public class LombokBuilderImpl {

    public static void main(String[] args) {
        BuilderAnnotation obj = new BuilderAnnotation(null, null, 0, null, null).builder().firstName("sada")
                .lastName("dad").age(12).phone("898808").address("adad").build();
        System.out.println(obj);
    }

}

I think every developer should use Project Lombok to get rid of boilerplate codes. Spring community also recommends using it. Please share your feedback in the comments below.