Java-Lombok: Do we need getters and setters?

Shreyas M N
The Startup
Published in
7 min readApr 3, 2020

--

Less Code, Less bugs !!

Hello Java enthusiasts, let’s talk about Java and refactoring. As we all know, Java remains the most popular programming language (well, I guess so) and reinventing itself adding more and more features in every release, becoming a perfect blend of object oriented and functional language flavors with the introduction of Lambda expressions, Streams and CompletableFuture in Java_8.

We are all aware of Java beans aka POJOs (Plain old java objects) aka DTOs (Data transfer objects). Even though the respective definitions vary in the vast java community, they all do the same thing. Basically different names, same concept.

Note: I will use the term DTO (Data transfer object) for Java beans aka POJO’s now onwards in this article.

Common traits of DTOs (Data transfer objects):

  1. All properties must be private.
  2. It should have a no-args constructor.
  3. It should be Serializable (in most cases, unless it will NOT be transferred over the network).
  4. It should provide ways to fetch(get) and change(set) the value of properties through getter and setter methods.

To summarize, DTOs are customized data types. And the above-mentioned contract is just a convention, It’s neither a Java standard nor a design pattern, after all, a design pattern is the best practice proved to be effective in approaching real world problems though.

So far, so good. But here is the problem. What if the DTO contains, let’s say 10 properties or more. Here is how the typical DTO looks like.

Let’s take a look at the sample Employee_DTO which contains basic information about an employee.

import java.util.Date;

public class Employee_DTO {

private int employeeId;
private String name;
private String email;
private String department;
private Employee_DTO manager;
private Date joiningDate;
private String designation;
private String address;
private int phoneNumber;
private int emergencyContact;
private int yearsAtCompany;
private int totalExperienceInYears;

public int getEmployeeId() {
return employeeId;
}

public void setEmployeeId(int employeeId) {
this.employeeId = employeeId;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

public String getDepartment() {
return department;
}

public void setDepartment(String department) {
this.department = department;
}

public Employee_DTO getManager() {
return manager;
}

public void setManager(Employee_DTO manager) {
this.manager = manager;
}

public Date getJoiningDate() {
return joiningDate;
}

public void setJoiningDate(Date joiningDate) {
this.joiningDate = joiningDate;
}

public String getDesignation() {
return designation;
}

public void setDesignation(String designation) {
this.designation = designation;
}

public String getAddress() {
return address;
}

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

public int getPhoneNumber() {
return phoneNumber;
}

public void setPhoneNumber(int phoneNumber) {
this.phoneNumber = phoneNumber;
}

public int getEmergencyContact() {
return emergencyContact;
}

public void setEmergencyContact(int emergencyContact) {
this.emergencyContact = emergencyContact;
}

public int getYearsAtCompany() {
return yearsAtCompany;
}

public void setYearsAtCompany(int yearsAtCompany) {
this.yearsAtCompany = yearsAtCompany;
}

public int getTotalExperienceInYears() {
return totalExperienceInYears;
}

public void setTotalExperienceInYears(int totalExperienceInYears) {
this.totalExperienceInYears = totalExperienceInYears;
}

}

We know it doesn’t look good with tens of getters and setters. Of course, it is one of those examples for spaghetti code and less readable as well. Imagine having tens or maybe hundreds of these DTOs and getters and setters for all of these DTOs. Let’s not imagine (yes, I mean it) since we do have an elegant way of overcoming it and achieve nevertheless the same with less boilerplate code.

This is where the Lombok comes into the picture. It gives us simple annotations which are easy to understand and helps us avoid all the boilerplate code one can imagine in the DTOs.

How the Lombok works behind the scenes:

Lombok reduces the boiler-plate via annotations that perform class transformations at compile time. Lombok comes with a decent set of transformations, but you may also want to create your own custom Lombok transformations.

Lombok runs as an annotation processor. The annotation processor acts as a dispatcher that delegates to Lombok annotation handlers (this is what we’re going to create). Handlers are discovered via a framework called SPI. Lombok annotation handlers declare the specific annotation that they handle. When delegating to a handler, the Lombok annotation processor provides an object representing the Abstract Syntax Tree (AST) of the annotated node (e.g. class, method, field, etc). The Lombok annotation handler is free to modify the AST by injecting new nodes such as methods, fields, and expressions. After the annotation processing stage, the compiler will generate byte code from the modified AST.

In short, Lombok doesn’t actually generate code at all. Instead, it uses unspecified and undocumented internal compiler implementation API calls to directly modify the program’s abstract syntax tree between reading the source code and outputting compiled byte-code.

Okay, the theory apart. Let’s see how quickly we can introduce Lombok in our Java project: The maven dependency for sure looks like this:

adding lombok dependency in the pom.xml

And the Employee_DTO which was illustrated above looks much more elegant and readable with Lombok. Let’s go through some examples which illustrates how Lombok annotations can be used and what they mean to us.

  1. Lombok getters and setters applied to the entire DTO:

@Getter and @Setterannotations are applied to the entire class as shown below.

import lombok.Getter;
import lombok.Setter;

import java.util.Date;

@Getter
@Setter
public class Employee_DTO_Lombok {

private int employeeId;
private String name;
private String email;
private String department;
private Employee_DTO manager;
private Date joiningDate;
private String designation;
private String address;
private int phoneNumber;
private int emergencyContact;
private int yearsAtCompany;
private int totalExperienceInYears;

}

2. Lombok getters and setters applied selectively in a given DTO:

And incase we only need getter and setter methods for selected variables / properties, we can do that as well.

getters and setters applied selectively

3. Lombok to replace constructors:

a) Replacing All-Args constructor:

Sometimes you do not want to expose setter methods and require the client to set the values only by calling the constructor

Sample Java DTO with All-args constructor.

All-args constructor without lombok

After replacing the All-args constructor in the above code snippet with the @AllArgsConstructor annotation, it essentially boils down to something like this.

All-args constructor with lombok

b) Replacing No-Args constructor:

And replacing No-Args constructor is no different.

No-args constructor with lombok

c) Defining Access specifiers to constructors:

When dealing with Kryo serialization and deserialization, it is necessary to have a no-args constructor with private access and fortunately Lombok addresses such scenarios as well, so here is an example where we can define access specifiers for constructors:

No-args constructor with access specifier

4. Lombok to generate toString() method:

It generates toString() method as well by default considering all the properties in the DTO while preserving their order.

toString() with lombok

when printed, it goes like this

Person_DTO(personName=John Smith, personAddress=4th Avenue-LA, phoneNum=1234567890, email=abc@xyz.com)

5. Lombok to generate equals() and hashCode() methods:

@EqualsAndHashCode generates implementations of the equals(Object other) and hashCode() methods. By default, it'll use all non-static, non-transient fields, but you can modify which fields are used (and even specify that the output of various methods is to be used) by marking type members with @EqualsAndHashCode.Include or @EqualsAndHashCode.Exclude. Alternatively, you can specify exactly which fields or methods you wish to be used by marking them with @EqualsAndHashCode.Include and using @EqualsAndHashCode(onlyExplicitlyIncluded = true).

equals() and hashCode() with lombok

6. @Data: A shortcut for @ToString, @EqualsAndHashCode, @Getter on all fields, @Setter on all non-final fields and @RequiredArgsConstructor

@RequiredArgsConstructor generates a constructor with 1 parameter for each field that requires special handling. All non-initialized final fields get a parameter, as well as any fields that are marked as @NonNull that aren't initialized where they are declared.

@Data with lombok

which is as good as mentioning all of the annotations listed in the below code snippet.

@Data explained

There are other annotations/utilities available in the project Lombok which are very useful and comes handy.

To name a few, @var, @val, @NonNull, @Cleanup, @Value, @Builder.

It is very easy to refactor the existing code/project and introduce the Lombok with considerably less effort without breaking anything. As mentioned earlier, it makes code look a lot cleaner and makes the existing code look elegant and simple.

Adding Lombok plugin in the IDEs:

Note: If you are using Eclipse or IntelliJ, just adding Lombok dependency in the pom.xml does not work all the time and Lombok plugin needs to be added manually to the respective IDE’s.

1. Adding Lombok plugin in the IntelliJ:

  1. Press Command + comma (“⌘” + “,” )
  2. Search “plugin” in the top left corner of the window
  3. Switch the tab i.e, Go to “marketplace
  4. Search for “lombok” and press “install
  5. Restart the IntelliJ
Adding lombok plugin in IntelliJ

2. Adding Lombok plugin in the Eclipse:

  1. Get the Lombok jar first. The latest version is located on Maven Central.
  2. Run the jar via java -jar command and an installer UI will open.
  3. Once we’ve selected the installations, then we press the Install/Update button and exit the installer.
  4. After installing the plugin, restart the Eclipse and ensure that Lombok is correctly configured.

Pain points:

  1. You might need to switch over to the ‘public wi-fi connection of your organization’, to be able to search and install the plugins sometimes.
  2. If you are connected to VPN, try disconnecting from it and add the plugin.
  3. Change the proxy settings in the IDE, if the above tricks don’t work and try adding the plugin again.
Interesting reads:1. Load Balancers: A deep dive2. Futures in Java: CompletableFuture

Happy refactoring!

Follow me Twitter, LinkedIn: Twitter, LinkedIn

Resources:

  1. https://projectlombok.org
  2. https://stackoverflow.com/questions/6107197/how-does-lombok-work
  3. http://notatube.blogspot.com/2010/12/project-lombok-creating-custom.html
  4. https://stackoverflow.com/questions/3295496/what-is-a-javabean-exactly
  5. https://www.baeldung.com/lombok-ide

--

--

Shreyas M N
The Startup

Software Engineer, -Passionate about building Scalable backend systems. https://www.linkedin.com/in/shreyasmn