Lombok - yes I know! You may be surprised, but I don’t mean the beautiful island in Indonesia :) - is one of the best Java library that can be used to reduce all the boilerplate code that you have to write each time in your POJO Java classes!

I couldn’t imagine myself programming Java classes without using lombok project, especially @Data annotation!

So, in this article, I will try to highlight the importance of lombok project and cover in depth every point related to lombok data annotation!

Let’s get started!

@Data Annotation Overview

Using lombok @Data can surely provide significant technical benefits to your Java project by reducing the required extra code that usually brings no real value to the business side of your application!

What is Lombok Data Annotation?

In short, @Data is a special annotation provided by lombok project in order to generate automatically all the extra code that is frequently associated with POJOs and Java beans!

You may wonder what is boilerplate code! Well, it is the repetitive code that we have to write each time like :

  • Getter methods for all fields

  • Setter methods for all fields

  • toString() method

  • equals() and hashCode() implementations

  • Constructor that initializes all final fields

Lombok project provides many annotations, but @Data is very special because it provides an implicit shortcut that bind together all the following annotations: @ToString, @EqualsAndHashCode, @Getter, @Setter, and @RequiredArgsConstructor!

How Lombok @Data Annotation Works?

Lombok hooks itself into the compilation phase and acts as an annotation processor that “auto generate” the extra code you need for your classes! The job of lombok is not to generate new files but to modify the existing ones!

By annotating your class with @Data, lombok will simply generate a fully parameterized constructor with setter, getter, equals, hashCode and toString methods for your class silently during compile time! Awesome, right ?

To work with lombok project, you need to install it first! Follow below steps to install lombok in eclipse (Windows OS):

  • Downloaded lombok jar from https://projectlombok.org/download

  • Go to the jar location, then execute the jar file or run this command line in your terminal: java -jar lombok.jar

  • Once the lombok window is opened, Specify the eclipse.exe location under eclipse installation folder!

  • Click on “Install/Update” button to start the installation process!

Install Lombok Project

If you are big fan of Maven like me, you need just to add the following dependency in your maven project pom.xml file in order to use lombok annotations!

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

Example Without Lombok @data Annotation

To show you what to do in Java without lombok data annotation, I will simply try to create a Person class which satisfies Java Beans conventions.

    
        package com.azhwani;
        import java.io.Serializable;
        public class Person implements Serializable {
            /**
            * 
            */
            private static final long serialVersionUID = 1L;
            // Properties
            private Long id;
            private String firstName;
            private String lastName;
            private String email;
            private int age;
            // Constructors
            public Person() { 
            }
            public Person(Long id, String firstName, String lastName, String email, int age) { 
                this.id = id;
                this.firstName = firstName;
                this.lastName = lastName;
                this.email = email;
                this.age = age;
            }
            // Getters + Setters
            public Long getId() {
                return id;
            }
            public void setId(Long id) {
                this.id = id;
            }
            public String getFirstName() {
                return firstName;
            }
            public void setFirstName(String firstName) {
                this.firstName = firstName;
            }
            public String getLastName() {
                return lastName;
            }
            public void setLastName(String lastName) {
                this.lastName = lastName;
            }
            public String getEmail() {
                return email;
            }
            public void setEmail(String email) {
                this.email = email;
            }
            public int getAge() {
                return age;
            }
            public void setAge(int age) {
                this.age = age;
            }
            // hashCode + equals + toString methods
            @Override
            public int hashCode() {
                final int prime = 31;
                int result = 1;
                result = prime * result + age;
                result = prime * result + ((email == null) ? 0 : email.hashCode());
                result = prime * result + ((firstName == null) ? 0 : firstName.hashCode());
                result = prime * result + ((id == null) ? 0 : id.hashCode());
                result = prime * result + ((lastName == null) ? 0 : lastName.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;
                Person other = (Person) obj;
                if (age != other.age)
                    return false;
                if (email == null) {
                    if (other.email != null)
                        return false;
                } else if (!email.equals(other.email))
                    return false;
                if (firstName == null) {
                    if (other.firstName != null)
                        return false;
                } else if (!firstName.equals(other.firstName))
                    return false;
                if (id == null) {
                    if (other.id != null)
                        return false;
                } else if (!id.equals(other.id))
                    return false;
                if (lastName == null) {
                    if (other.lastName != null)
                        return false;
                } else if (!lastName.equals(other.lastName))
                    return false;
                return true;
            }
            @Override
            public String toString() {
                return "Person [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + ", email=" + email
                        + ", age=" + age + "]";
            }
        }
    

Oops! Already more than 100 lines of code for a class with 5 properties!!

Person class has just 5 properties, so it is normally a basic simple Java class! However, as you can see, with the extra code of getters and setters methods … we ended up with a quite overwhelming boilerplate zero-value code!

You may say that most Java IDE can also auto generate getters and setters (even more) for you with just few clicks. Yes you are right! But, the generated code needs to live in your sources and must be maintainable and maintained!

Example with Lombok @data Annotation!

With lombok project, we will be able to focus only on what is important in our class: its data and their representation. The rest will be generated by lombok using @Data annotation.

Now, let’s take a close look on how we can use @Data annotation in our simple class Person!

    
        package com.azhwani;
        import lombok.Data;
        @Data
        public class Person  {
            private Long id;
            private String firstName;
            private String lastName;
            private String email;
            private int age;

        }
    

And that’s how you can go from more than 100 lines of code to just 9 lines! Awesome, right?

The code is more clearer and concise that way! As you can see, there is less code to write and maintain! Lombok does not stop here, it can provide more flexibility and readability to our code.

Lombok data example

@Data annotation is like having the following implicit annotations @Getter, @Setter, @ToString, @EqualsAndHashCode and @RequiredArgsConstructor!

    
        package com.azhwani;
        import lombok.EqualsAndHashCode;
        import lombok.Getter;
        import lombok.RequiredArgsConstructor;
        import lombok.Setter;
        import lombok.ToString;
        
        @Getter
        @Setter
        @ToString
        @EqualsAndHashCode
        @RequiredArgsConstructor
        public class Person  {
            private Long id;
            private String firstName;
            private String lastName;
            private String email;
            private int age;

        }
    

Demo

    
        package com.azhwani;
        public class LombokDataTest {
            public static void main(String[] args) {
                // TODO Auto-generated method stub
                Person pers = new Person();

                pers.setId(1L);
                pers.setFirstName("Jean");
                pers.setLastName("Deep");
                pers.setEmail("jean.deep@hotmail.com");
                pers.setAge(25);
           
                System.out.println("pers Hash Code: "+ pers.hashCode());
                System.out.println("Person Id : "+pers.getId());
                System.out.println("Person First Name : "+pers.getFirstName());
                System.out.println("Person Email : "+pers.getEmail());
                
                System.out.println(pers.toString());
                
                Person pers2 = new Person();
                pers.setId(1L);
                pers.setFirstName("Samantha");
                pers.setLastName("Collins");
                pers.setEmail("samantha.bella@gmail.com");
                pers.setAge(30);
                
                System.out.println("pers2 Hash Code: "+ pers2.hashCode());

                System.out.println("Equals method : "+ pers.equals(pers2));    
            } 
        }
        
        # Output : 
        pers Hash Code: -1414117140
        Person Id : 1
        Person First Name : Jean
        Person Email : jean.deep@hotmail.com
        Person(id=1, firstName=Jean, lastName=Deep, email=jean.deep@hotmail.com, age=25)
        pers2 Hash Code: 1244954339
        Equals method : false
    

Create Static Factory Method Using Lombok Data Annotation

With some adjustments added to our previous example, we can end up with a class that can only be instantiated by means of a static factory method!

Using staticConstructor attribute of @Data annotation will tell lombok to generate a private constructor and a public static factory method that will be used to create instances!

    
        package com.azhwani;
        import lombok.Data;
        @Data(staticConstructor = "of")
        public class Person  {
            private Long id;
            private String firstName;
            private String lastName;
            private String email;
            private int age;

        }
    

The name of the static method is “of”! Here the “of” convention is considered a best practice because it is heavily used in the new Java 8 APIs! However, we can also use the old conventions like newInstance

That way, we create our instances using Person.of() instead of new Person().

Demo

    
        package com.azhwani;
        public class LombokDataStaticFactoryTest {
            public static void main(String[] args) {
                Person ovlperson = Person.of();
                ovlperson.setId(1L);
                ovlperson.setFirstName("Olivia");
                ovlperson.setLastName("Wright");
                ovlperson.setEmail("olivia.queen@yahoo.com");
                ovlperson.setAge(30);
                
                System.out.println(ovlperson.toString());
                
                System.out.println("ovlperson Hash Code: "+ ovlperson.hashCode());
                System.out.println("Person Id : "+ovlperson.getId());
                System.out.println("Person First Name : "+ovlperson.getFirstName());
                System.out.println("Person Last Name : "+ovlperson.getLastName());
                System.out.println("Person Age : "+ovlperson.getAge());
            } 
        }
        
        # Output : 
        Person(id=1, firstName=Olivia, lastName=Wright, email=olivia.queen@yahoo.com, age=30)
        ovlperson Hash Code: 357966216
        Person Id : 1
        Person First Name : Olivia
        Person Last Name : Wright
        Person Age : 30
    

@Value vs @Data

The big difference between @Value and Lombok data annotation is that @Value is mainly used to create immutable objects unlike @Data!

@Value is a also an all-in-one annotation that combines @Getter, @AllArgsConstructor, @ToString and @EqualsAndHashCode and @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) annotations.

As you can see @Value does not contain @Setter annotation! @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) allow to make the fields private and final!

    
        package com.azhwani;
        import lombok.Value;
        
        @Value
        public class Person  {
            private String name;
            private int section;
        }
    

The Expanded version of @Value annotation looks like bellow:

    
        package com.azhwani;
        import lombok.Getter;
        import lombok.ToString;
        import lombok.EqualsAndHashCode;
        import lombok.experimental.FieldDefaults;;
        import lombok.AllArgsConstructor;
        
        @Getter
        @ToString
        @EqualsAndHashCode
        @FieldDefaults(makeFinal = true)
        @AllArgsConstructor
        public class Student  {
            private String name;
            private int section;
        }
    

If we de-lombok the @Value annotation, we will get:

    
        
        public final class Student {
            private final String name;
            private final int section;
            
            public Student(String name, int section) {
                this.name = name;
                this.section = section;
            }
            public String getName() {
             return this.name;
            }
            public int getSection() {
                return this.section;
            }
            @Override
            public boolean equals(final Object obj) {
                if (this == obj)
                    return true;
                if (obj == null)
                    return false;
                if (getClass() != obj.getClass())
                    return false;
                Student other = (Student) obj;
                if (name == null) {
                    if (other.name != null)
                        return false;
                } else if (!name.equals(other.name))
                    return false;
                if (section != other.section)
                    return false;
                return true;
            }
            @Override
            public int hashCode() {
                final int prime = 31;
                int result = 1;
                result = prime * result + ((name == null) ? 0 : name.hashCode());
                result = prime * result + section;
                return result;
            }
            @Override
            public String toString() {
                return "Student [name=" + name + ", section=" + section + "]";
            }
        }
    

Conclusion

That’s all! In this article, you learned what is lombok data annotation in details and how you can use it in your Java projects.

I hope you have found this article helpful,enlightening and inspiring to convince you give lombok a chance to get into your Java development toolset.

Thanks for reading. See you soon!