Java Lombok Tutorial | Part 1

Project Lombok


Project Lombok is a small but very useful library for java projects to reduce boiler plate code.
Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java. Never write another getter or equals method again. Early access to future java features such as val, and much more.
"Boilerplate" is a term used to describe code that is repeated in many parts of an application with little alteration


Common Java Object Functionality with Project Lombok



How does Java Project Lombok work?

Project Lombok is a small library that can be used to reduce the amount of boilerplate Java code that is commonly written for Java classes.  Project Lombok does this via annotations that can be added to the Java class for which common methods are desired.  Most of the annotations are self-descriptive in their names: @Getter, @Setter, @EqualsAndHashCode, @ToString, and @NoArgsConstructor are examples.


Why Project Lombok required?
package tutorial.interviewbubble.LombokSampleApplication;


public class Person {


private String firstName;
private String lastName;
private int age;
private double salary;
public Person() {
}
public Person(String firstName, String lastName, int age, double salary) {
super();
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.salary = salary;
}
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 int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Person [firstName=" + firstName + ", lastName=" + lastName + ", age=" + age + ", salary=" + salary
+ "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((firstName == null) ? 0 : firstName.hashCode());
result = prime * result + ((lastName == null) ? 0 : lastName.hashCode());
long temp;
temp = Double.doubleToLongBits(salary);
result = prime * result + (int) (temp ^ (temp >>> 32));
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 (firstName == null) {
if (other.firstName != null)
return false;
} else if (!firstName.equals(other.firstName))
return false;
if (lastName == null) {
if (other.lastName != null)
return false;
} else if (!lastName.equals(other.lastName))
return false;
if (Double.doubleToLongBits(salary) != Double.doubleToLongBits(other.salary))
return false;
return true;
}
}


Person class should have getter-setters for the instance variables, equals & hashCode method implementation, all field constructors and an implementation of toString method. This class so far has no business logic and even without it is 80+ lines of code. This is insane.
---------------------------

package tutorial.interviewbubble.LombokSampleApplication;

public class Person {

private String firstName;
private String lastName;
private int age;
private double salary;
}

package tutorial.interviewbubble.LombokSampleApplication;

/**
 * Driver Application to test Lombok Project
 *
 */
public class App 
{
    public static void main( String[] args )
    {
    Person person = new Person(); 
System.out.println( person );
    }
}


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>tutorial.interviewbubble</groupId>
  <artifactId>LombokSampleApplication</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>LombokSampleApplication</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.16</version>
<scope>provided</scope>
</dependency>
  </dependencies>
</project>


------------------------------
@ToString() Annotation:
When the above class is generated and its implicitly inherited (from ObjecttoString() method is called, the output looks like that shown in the next image.
tutorial.interviewbubble.LombokSampleApplication.Person@3d4eac69

We could write an explicit toString() method or use Project Lombok. The next code snippet demonstrates the Project Lombok approach.
package tutorial.interviewbubble.LombokSampleApplication;

import lombok.ToString;

@ToString
public class Person {

private String firstName;
private String lastName;
private int age;
private double salary;
}

package tutorial.interviewbubble.LombokSampleApplication;

/**
 * Driver Application to test Lombok Project
 *
 */
public class App 
{
    public static void main( String[] args )
    {
    Person person = new Person(); 
System.out.println( person );
    }
}
Person(firstName=null, lastName=null, age=0, salary=0.0)

@NoArgsConstructor Annotation

package tutorial.interviewbubble.LombokSampleApplication;

import lombok.NoArgsConstructor;

@NoArgsConstructor
public class Person {

private String firstName;
private String lastName;
private int age;
private double salary;
}

package tutorial.interviewbubble.LombokSampleApplication;

/**
 * Driver Application to test Lombok Project
 *
 */
public class App 
{
    public static void main( String[] args )
    {
    Person person = new Person(); 
System.out.println( "Printing Object: "+person );
    }
}

Output:
Printing Object: tutorial.interviewbubble.LombokSampleApplication.Person@3d4eac69



@NoArgsConstructor Annotation with Final Field
@NoArgsConstructor will generate a constructor with no parameters. If this is not possible (because of final fields), a compiler error will result instead, unless @NoArgsConstructor(force = true) is used, then all final fields are initialized with 0 / false / null

package tutorial.interviewbubble.LombokSampleApplication;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
/* in Project use only import lombok.* here i am using All imports to show you which classes from lomlok project we are using */
@NoArgsConstructor
@AllArgsConstructor
@ToString

public class Person {
@Getter
final private String firstName;
final private String lastName;
@Getter
private int age;
@Setter
private double salary;

}

package tutorial.interviewbubble.LombokSampleApplication;

/**
 * Driver Application to test Lombok Project
 *
 */
public class App 
{
    public static void main( String[] args )
    {

    Person person = new Person();
  
    System.out.println(person);

    }

}


Output 
Exception in thread "main" java.lang.Error: Unresolved compilation problems: 
The blank final field firstName may not have been initialized
The blank final field lastName may not have been initialized

at tutorial.interviewbubble.LombokSampleApplication.Person.(Person.java:9)
at tutorial.interviewbubble.LombokSampleApplication.App.main(App.java:12)


package tutorial.interviewbubble.LombokSampleApplication;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
/* in Project use only import lombok.* here i am using All imports to show you which classes from lomlok project we are using */
@NoArgsConstructor
@AllArgsConstructor
@ToString

public class Person {
@Getter
final private String firstName="Tom";
final private String lastName="Cruse";
@Getter
private int age;
@Setter
private double salary;

}

package tutorial.interviewbubble.LombokSampleApplication;

/**
 * Driver Application to test Lombok Project
 *
 */
public class App 
{
    public static void main( String[] args )
    {

    Person person = new Person();
  
    System.out.println(person);

    }

}

Output:

Person(firstName=Tom, lastName=Cruse, age=0, salary=0.0)


package tutorial.interviewbubble.LombokSampleApplication;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
/* in Project use only import lombok.* here i am using All imports to show you which classes from lomlok project we are using */
@NoArgsConstructor(force = true)
@AllArgsConstructor
@ToString

public class Person {
@Getter
final private String firstName="Tom";
final private String lastName;  // unintialize final variable
@Getter
private int age;
@Setter
private double salary;

}

package tutorial.interviewbubble.LombokSampleApplication;

/**
 * Driver Application to test Lombok Project
 *
 */
public class App 
{
    public static void main( String[] args )
    {

    Person person = new Person();
  
    System.out.println(person);

    }

}

Output: Person(firstName=Tom, lastName=null, age=0, salary=0.0)




@AllArgsConstructor Annotation


package tutorial.interviewbubble.LombokSampleApplication;

import lombok.AllArgsConstructor;
import lombok.ToString;
/* in Project use only import lombok.* here i am using All imports to show you which classes from lomlok project we are using */
@ToString
@AllArgsConstructor
public class Person {

private String firstName;
private String lastName;
private int age;
private double salary;

}

package tutorial.interviewbubble.LombokSampleApplication;

/**
 * Driver Application to test Lombok Project
 *
 */
public class App 
{
    public static void main( String[] args )
    {
    Person person = new Person("Tom", "Cruse", 40, 10000000.00); 
System.out.println( "Printing Object: "+person );
    }
}

Output: 
Printing Object: Person(firstName=Tom, lastName=Cruse, age=40, salary=1.0E7)

Note:
package tutorial.interviewbubble.LombokSampleApplication;

/**
 * Driver Application to test Lombok Project
 *
 */
public class App 
{
    public static void main( String[] args )
    {
    Person person = new Person(); 
System.out.println( person );
    }

}

package tutorial.interviewbubble.LombokSampleApplication;

import lombok.AllArgsConstructor;

@AllArgsConstructor
public class Person {

private String firstName;
private String lastName;
private int age;
private double salary;
}

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
The constructor Person() is undefined

at tutorial.interviewbubble.LombokSampleApplication.App.main(App.java:11)


package tutorial.interviewbubble.LombokSampleApplication;

import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.ToString;
/* in Project use only import lombok.* here i am using All imports to show you which classes from lomlok project we are using */
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Person {

private String firstName;
private String lastName;
private int age;
private double salary;

}



package tutorial.interviewbubble.LombokSampleApplication;

/**
 * Driver Application to test Lombok Project
 *
 */
public class App 
{
    public static void main( String[] args )
    {
    Person person = new Person(); 
System.out.println( "Printing Object: "+person );
    }

}

OutPut:

Printing Object: Person(firstName=null, lastName=null, age=0, salary=0.0)



A class like Person.java is often a data class that will need to be used in comparisons and possibly hashCode-based collection keys. It is important to create equals(Object) and hashCode()implementations correctly and to make sure they are created together. Because there are default equals and hashCode methods provided by the parent Object class, Java code using Person instances will be able to perform equals and/or hashCode, but they aren't likely to be what one really wants. When the Main executable class is changed to the next code listing, we see the output after that which tells us that the equality comparison is being done completely based on identity rather than on content.


package tutorial.interviewbubble.LombokSampleApplication;

import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
/* in Project use only import lombok.* here i am using All imports to show you which classes from lomlok project we are using */
@NoArgsConstructor
@AllArgsConstructor
public class Person {

private String firstName;
private String lastName;
private int age;
private double salary;

}

package tutorial.interviewbubble.LombokSampleApplication;

/**
 * Driver Application to test Lombok Project
 *
 */
public class App 
{
    public static void main( String[] args )
    {

    Person person1 = new Person("Tom", "Cruse", 45, 32948292);
Person person2 = new Person("Tom", "Cruse", 45, 32948292);
    
if (person1.equals(person2))
    {
    System.out.println("Same person!");
    }
    else
    {
    System.out.println("Different people!");
    }
    }
}

Output: 
Different people!


This is almost never what is wanted here. Instead, an explicit equals implementation is required. I like the fact that the Lombok annotation for this, @EqualsAndHashCode, only generates both of these together because it does not make sense to explicitly override them individually. The Person.java class listing is shown next with the addition of the @EqualsAndHashCode annotation.


package tutorial.interviewbubble.LombokSampleApplication;

import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
/* in Project use only import lombok.* here i am using All imports to show you which classes from lomlok project we are using */
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class Person {

private String firstName;
private String lastName;
private int age;
private double salary;

}

package tutorial.interviewbubble.LombokSampleApplication;

/**
 * Driver Application to test Lombok Project
 *
 */
public class App 
{
    public static void main( String[] args )
    {

    Person person1 = new Person("Tom", "Cruse", 45, 32948292);
Person person2 = new Person("Tom", "Cruse", 45, 32948292);
    
if (person1.equals(person2))
    {
    System.out.println("Same person!");
    }
    else
    {
    System.out.println("Different people!");
    }
    }
}

Output:
Same person!


@Getter and @Setter Anotation:


package tutorial.interviewbubble.LombokSampleApplication;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/* in Project use only import lombok.* here i am using All imports to show you which classes from lomlok project we are using */
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
public class Person {

private String firstName;
private String lastName;
private int age;
private double salary;

}

package tutorial.interviewbubble.LombokSampleApplication;

/**
 * Driver Application to test Lombok Project
 *
 */
public class App 
{
    public static void main( String[] args )
    {

    Person person = new Person();
    person.setFirstName("Tom");
    person.setAge(35);;

    System.out.println("Name :" + person.getFirstName()+ ", Age :" +person.getAge());
    }

}



-------
package tutorial.interviewbubble.LombokSampleApplication;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
/* in Project use only import lombok.* here i am using All imports to show you which classes from lomlok project we are using */
@NoArgsConstructor
@AllArgsConstructor
@ToString

public class Person {
@Getter
@Setter
private String firstName;
private String lastName;
@Getter
private int age;
@Setter
private double salary;
}

package tutorial.interviewbubble.LombokSampleApplication;

/**
 * Driver Application to test Lombok Project
 *
 */
public class App 
{
    public static void main( String[] args )
    {

    Person person = new Person(null,"Cruse",45,0);
    person.setFirstName("Tom");
    person.setSalary(23443233);

    System.out.println("Name :" + person.getFirstName()+ ", Age :" +person.getAge());
    System.out.println(person);

    }

}

Output:
Name :Tom, Age :45
Person(firstName=Tom, lastName=Cruse, age=45, salary=2.3443233E7)



@NotNull Annotation:

package tutorial.interviewbubble.LombokSampleApplication;

import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
import lombok.ToString;
/* in Project use only import lombok.* here i am using All imports to show you which classes from lomlok project we are using */
@ToString
@Getter
@Setter
public class Person {
private String firstName;
@NonNull private String lastName;
private int age;
private double salary;
public Person() {
super();
lastName = null;
}

}


package tutorial.interviewbubble.LombokSampleApplication;

/**
 * Driver Application to test Lombok Project
 *
 */
public class App 
{
    public static void main( String[] args )
    {

    Person person = new Person();
  
    System.out.println("Printing Not Null Field: "+person.getLastName());

    }


}


Output: Printing Not Null Field: null



Does @NonNull does not hold on default constructors?
Indeed no, it doesn't, When a default constructor is provided by the compiler, that happens during compilation, after annotation processing. you will provide then also not
Reading the documentation, it seems that a manual constructor prevents the null checks on a field, but this should be highlighted much more strongly.


That is, the annotation has effect only on your own constructors and methods (i.e. those present in your source code) and those Lombok generates for you. A default constructor provided by the compiler is neither.

Lombok has always treated any annotation named @NonNull on a field as a signal to generate a null-check if lombok generates an entire method or constructor for you, via for example @Data. Now, however, using lombok's own @lombok.NonNull on a parameter results in the insertion of just the null-check statement inside your own method or constructor.
for example @NonNull on a primitive parameter results in a warning. No null-check will be generated. And  @NonNull on a parameter of an abstract method used to generate a warning


Because null is a reference. And primitive types are not reference types. Only objects are reference types.
Just remember that primitive data types cannot be null.

Never

Never


Never


package tutorial.interviewbubble.LombokSampleApplication;

import lombok.NonNull;

public class Person {
public String fullName(@NonNull String firstName,String MiddleName, String lastName, @NonNull int a) {
return firstName+" "+MiddleName+" "+lastName;
}
}



package tutorial.interviewbubble.LombokSampleApplication;

import lombok.NonNull;

public class Person {
public String fullName(@NonNull String firstName,String MiddleName, String lastName, @lombok.NonNull int a) {
return firstName+" "+MiddleName+" "+lastName;
}

}




You can use @NonNull on the parameter of a method or constructor to have lombok generate a null-check statement for you.

package tutorial.interviewbubble.LombokSampleApplication;

import lombok.NonNull;

public class Person {
public String fullName(@NonNull String firstName,String MiddleName, String lastName) {
return firstName+" "+MiddleName+" "+lastName;
}
}


package tutorial.interviewbubble.LombokSampleApplication;

/**
 * Driver Application to test Lombok Project
 *
 */
public class App 
{
    public static void main( String[] args )
    {

    Person person = new Person();
    String nullfirstName = null;
    String nullMiddleName = null;
    String str = person.fullName(nullfirstName,nullMiddleName, "Welch");
  
    System.out.println("Printing String "+str);

    }

}


Output:
Exception in thread "main" java.lang.NullPointerException: firstName
at tutorial.interviewbubble.LombokSampleApplication.Person.fullName(Person.java:7)
at tutorial.interviewbubble.LombokSampleApplication.App.main(App.java:15)

package tutorial.interviewbubble.LombokSampleApplication;

import lombok.NonNull;

public class Person {
public String fullName(@NonNull String firstName,String MiddleName, String lastName) {
return firstName+" "+MiddleName+" "+lastName;
}
}

package tutorial.interviewbubble.LombokSampleApplication;

/**
 * Driver Application to test Lombok Project
 *
 */
public class App 
{
    public static void main( String[] args )
    {

    Person person = new Person();
    String nullfirstName = "Tom";
    String nullMiddleName = null;
    String str = person.fullName(nullfirstName,nullMiddleName, "Welch");
  
    System.out.println("Printing String "+str);

    }

}

OutPut:

Printing String Tom null Welch


Risks?

One could argue that having all those fancy stuff done by a compiler could lead to unexpected results and probably introduces errors nobody likes. Right. But even if you don't like all those automated and transparent generation, you could simply Delombok your source files. This would simply replace all lombok annotations with the generated code and gives your access to a complete codebase. So it's a calculable risk using Lombok and I must admit: I love it.



Comments