Last week we looked at Java 14 records as a way to easily create immutable data classes to use as DTOs (data transfer objects). This week we’re going to look at doing something similar for mutable domain objects.
It’s normally necessary to create a canonical class for each domain object in our design. But writing all the code for these classes can be quite tedious. Imagine that we have a large number of domain classes in our design. To create a canonical class for each one of them will entail a huge amount of repetitive coding.
Project Lombok (https://projectlombok.org/) comes to the rescue!
What is Project Lombok?
Project Lombok is a small third party library that we add to the CLASSPATH. Lombok defines a number of annotations and associated annotation processors. These annotations are used to annotate our domain classes appropriately, instructing the annotation processors to generate any necessary boilerplate code. This turns a simple data structure into a canonical class.
The annotations include:
@Getter/@Setter@ToString@EqualsAndHashCode@NoArgsConstructordefines the default no-argument constructor@RequiredArgsConstructordefines a constructor with an argument for each final or non-null field@AllArgsConstructordefines a constructor with one argument for every field@Datais a shortcut for a@Getteron every field, a@Setteron all non-final fields,@ToString,@EqualsAndHashCodeand@RequiredArgsConstructor
Implementation
We would use the annotations as follows:
import lombok.*;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {
// simple data structure with no additional code
private String name;
private int age;
private Gender gender;
} // end of class
Download the lombok.jar file at https://projectlombok.org/download. Once we’ve added it to the CLASSPATH, we’re ready to go.
Generated Code
Once we’ve compiled the code, we can run the JDK javap disassembler on the Person.class file. The default output of javap is the following:
public class Person {
public java.lang.String getName();
public int getAge();
public Gender getGender();
public void setName(java.lang.String);
public void setAge(int);
public void setGender(Gender);
public boolean equals(java.lang.Object);
protected boolean canEqual(java.lang.Object);
public int hashCode();
public java.lang.String toString();
public Person();
public Person(java.lang.String, int, Gender);
}
As we can see, we now have a default no-argument constructor, a constructor with a parameter for each of the three defined fields, appropriate getters and setters (using the JavaBeans naming convention), as well as the equals(), hashCode(), and toString() methods.
We’re not compelled to have Lombok generate all the code. We can choose to create our own constructors or equals() and hashCode() methods, if we so choose.
To see what code the Lombok annotations actually generate, go to http://www.javadecompilers.com/, choose just about any decompiler (depending on the Java version being used), and upload the compiled Person class. The resulting code may or may not be what we would like for our canonical classes.
Lombok vs IDE Code Generation
If we’re using a modern IDE, it can also generate code for domain classes. A problem with actual code generation is that the code is physically there in the project. We can see what’s generated, but we also have a lot more code to wade through when maintaining and modifying code.
If we add extra fields to our class, the IDE doesn’t automatically re-generate the code. Lombok doesn’t generate Java code – it generates bytecodes on the fly. So the simple data structure that we created earlier is the only code we need to see.
Conclusion
Using Project Lombok can lead to much faster coding for domain classes, with a lot less code to maintain and wade through. It is currently used in a large number of enterprise applications.
This post is not necessarily an endorsement of Project Lombok, though personally I think it’s very cool! It’s to introduce the possibility of using it in a project if it suits our needs.
As always, please share your comments and questions.