Skip to content
Dev Discovers

Streamlining Java Development with Lombok Annotations

java7 min read

Lombok is a Java library that simplifies the process of writing Java code by reducing boilerplate code, such as getters, setters, constructors, and more. Lombok helps to improve productivity and code readability by providing annotations that generate code during compilation time. In this post, we will explore the basics of Lombok, how to install it, and some of the most commonly used Lombok annotations.

Installing Lombok

Before we dive into the different annotations Lombok offers, let's first look at how to install Lombok into your Java project.

The easiest way to install Lombok is by using a dependency management tool such as Maven or Gradle. To use Lombok with Maven, add the following dependency to your pom.xml file:

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>

To use Lombok with Gradle, add the following dependency to your build.gradle file:

compileOnly 'org.projectlombok:lombok:1.18.20'
annotationProcessor 'org.projectlombok:lombok:1.18.20'

Common Lombok Annotations

@Getter and @Setter

The @Getter and @Setter annotations generate getter and setter methods for fields in your Java classes, eliminating the need to write them manually. For example:

@Getter
@Setter
public class Person {
private String firstName;
private String lastName;
}

This generates the following code during compilation time:

public class Person {
private String firstName;
private String lastName;
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;
}
}

@NoArgsConstructor and @AllArgsConstructor

The @NoArgsConstructor and @AllArgsConstructor annotations generate constructors with no arguments and all arguments, respectively. For example:

@NoArgsConstructor
@AllArgsConstructor
public class Person {
private String firstName;
private String lastName;
private int age;
}

This generates the following code during compilation time:

public class Person {
private String firstName;
private String lastName;
private int age;
public Person() {
}
public Person(String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
}

@NonNull

The @NonNull annotation is used to add null-checks to method or constructor parameters, generating a NullPointerException if a null value is passed. This annotation can be useful in preventing null pointer exceptions in your code.

Here is an example of how to use the @NonNull annotation:

public class Person {
private final String name;
public Person(@NonNull String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}

In the example above, the @NonNull annotation is used on the name parameter of the Person constructor. This ensures that a NullPointerException will be thrown if null is passed as the name parameter.

During compilation, Lombok generates the following code for the Person class:

public class Person {
private final String name;
public Person(@NonNull String name) {
Objects.requireNonNull(name, "name is marked @NonNull but is null");
this.name = name;
}
public String getName() {
return this.name;
}
}

As you can see, Lombok generates the null-check for the name parameter using the Objects.requireNonNull method.

@EqualsAndHashCode

The @EqualsAndHashCode annotation generates equals and hashCode methods for the class based on the specified fields. This annotation can be useful in simplifying the code for checking equality between objects.

Here is an example of how to use the @EqualsAndHashCode annotation:

@EqualsAndHashCode
public class Person {
private final String name;
private final int age;
}

In the example above, the @EqualsAndHashCode annotation is used on the Person class. This generates equals and hashCode methods for the class based on the name and age fields.

During compilation, Lombok generates the following code for the Person class:

public class Person {
private final String name;
private final int age;
public boolean equals(final Object o) {
if (o == this) {
return true;
}
if (!(o instanceof Person)) {
return false;
}
final Person other = (Person) o;
if (!other.canEqual((Object) this)) {
return false;
}
final Object this$name = this.name;
final Object other$name = other.name;
if (this$name == null ? other$name != null : !this$name.equals(other$name)) {
return false;
}
if (this.age != other.age) {
return false;
}
return true;
}
protected boolean canEqual(final Object other) {
return other instanceof Person;
}
public int hashCode() {
final int PRIME = 59;
int result = 1;
final Object $name = this.name;
result = result * PRIME + ($name == null ? 43 : $name.hashCode());
result = result * PRIME + this.age;
return result;
}
}

As you can see, the generated equals method checks for equality of the name and age fields, and the hashCode method generates a hash code based on the same fields.

@Builder

The @Builder annotation generates a builder pattern for a class, allowing for easy creation of complex objects with many fields. The builder pattern is a creational design pattern that separates the construction of a complex object from its representation, allowing for more flexible and readable code. With the @Builder annotation, we can generate a builder class that can be used to set values for each field in a fluent and easy-to-read way.

Let's consider a simple example. Suppose we have a class called Person with three fields: name, age, and email.

@Builder
public class Person {
private String name;
private int age;
private String email;
}

Here, we've annotated our class with @Builder. This generates a builder class that we can use to create instances of Person. We can now create a new instance of Person using the builder as follows:

Person person = Person.builder()
.name("John")
.age(30)
.email("john@example.com")
.build();

In this example, we're using the builder to set the values of each field. The builder returns a Person object with the specified values.

Here is the compilation output of the Person class:

public class Person {
private String name;
private int age;
private String email;
Person(String name, int age, String email) {
this.name = name;
this.age = age;
this.email = email;
}
public static PersonBuilder builder() {
return new PersonBuilder();
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
public String getEmail() {
return this.email;
}
public static class PersonBuilder {
private String name;
private int age;
private String email;
PersonBuilder() {
}
public Person.PersonBuilder name(String name) {
this.name = name;
return this;
}
public Person.PersonBuilder age(int age) {
this.age = age;
return this;
}
public Person.PersonBuilder email(String email) {
this.email = email;
return this;
}
public Person build() {
return new Person(this.name, this.age, this.email);
}
public String toString() {
return "Person.PersonBuilder(name=" + this.name + ", age=" + this.age + ", email=" + this.email + ")";
}
}
}

@Data

The @Data annotation generates all the getters, setters, equals, hashCode, and toString methods for the class. This can reduce boilerplate code in classes that require these methods.

@Data
public class Person {
private String name;
private int age;
}

When the @Data annotation is applied to the Person class, Lombok generates the following code during compilation:

public class Person {
private String name;
private int age;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return this.age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

Final Thoughts

The annotations discussed in this article are just a few examples of the many annotations available in Lombok that can help simplify the code. Please visit the Lombok documentation to discover more useful annotations!

By using Lombok, developers can focus more on writing the logic of their application rather than worrying about the repetitive and tedious aspects of Java development. Overall, Lombok is a valuable addition to any Java developer's toolkit and can greatly improve the efficiency and readability of their code.

© 2023 by Dev Discovers. All rights reserved.
Theme by LekoArts