Project Lombok Tutorial
How To Use @Data Annotation With Project Lombok in Java Applications
@Data
is an "all-in-one" special annotation that groups functionalities from multiple annotations namely:-
# Dev Environment:
- Apache maven (v3.6.1)
- maven-compiler-plugin (v3.8.1)
- lombok (v1.18.8)
- Apache Netbeans (v10.0)
TIP: For those who would like to follow along with this blog, just clone my repository git clone https://github.com/steven7mwesigwa/java-tutorials.git
and navigate to java-tutorials\project-lombok\data
. In there, you should be able to play around with all the source code used in this blog post.
# Demonstration : (Without Lombok)
Before we move on, let's look at how we would normally type out code for a regular Java object without using any lombok specific annotations.
Let's create a Person
class that will have a manual implementation of @Data
annotation. Full code here
//demo1
package com.stevenmwesigwa.data.demo1;
import java.util.Objects;
public class Person {
private String firstname;
private String lastname;
public Person() {
}
public String getFirstname() {
return firstname;
}
public String getLastname() {
return lastname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
@Override
public int hashCode() {
int hash = 7;
hash = 17 * hash + Objects.hashCode(this.firstname);
hash = 17 * hash + Objects.hashCode(this.lastname);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Person other = (Person) obj;
if (!Objects.equals(this.firstname, other.firstname)) {
return false;
}
if (!Objects.equals(this.lastname, other.lastname)) {
return false;
}
return true;
}
protected boolean canEqual(final Object other) {
return other instanceof Person;
}
@Override
public String toString() {
return "Person{" + "firstname=" + firstname + ", lastname=" + lastname + '}';
}
}
As you would notice, this is a lot of boilerplate code for just 2 fields in our Person class.
It was actually even more painful for me to paste it here, but i had no choice but to show you how big your code can get.
For those who aren't familiar with what's going on, am basically providing:-
- getters - To be able to return field values.
- setters - To be able to set field values.
- toString method - To return a more readable string representation of our object.
- equals method - To check if 2 objects are equal.
- hashCode method - Simply returns a hash code value for the object.
- canEqual method - Explained in this equality method paper.
- A "required args constructor" - A special constructor that takes
one parameter for each
final
or non-null field with no initial value. Since we don't have any final / non-null fields, we don't have any parameters for our constructor.
Now, enough with the manual implementation. Let's try out some magic provided by Lombok specific annotations.
# Using @Data
Annotation From Project Lombok Library : (Lombok)
The project Lombok authors decided to work on a quick solution to eliminate all this boilerplate code with just one annotation.
Adding @Data
annotation right on top of your class informs Lombok to automatically generate:-
- Equals and hashCode methods.
- Setters for each non-final field.
- A toString method.
- A constructor that takes one parameter for each
final
or non-null field with no initial value. - Getters for each field.
Let's create a Person
class, but this time making use of @Data
annotation... Full code here
//demo2
package com.stevenmwesigwa.data.demo2;
import lombok.Data;
@Data
public class Person {
private String firstname;
private String lastname;
}
Imagine if i had told you that by annotating my class with @Data
annotation, Lombok would generate all the functionalities we had manually implemented in our previous example.
You can now see how small our code looks. As they say, less code less bugs, this allows us to focus on more important parts of our logic.
# How To Include Other Lombok Specific Annotations On Top Of @Data
annotation
Now let's move on to the tricky parts. I know some of you're having several questions like:-
- What if i want my class to be read only?. Hence, no setters.
- What if i want my class to be write-only?. Hence, no getters.
- What if i want my class to have an "all args constructor" or a "no args constructor"?.
- What if i want my
toString
method to include 'field names' in its returned string representation of my object?. - What if i want to change the access level for either my setters or getters?.
- etc...
Truthfully speaking, we don't have special parameters for @Data
annotation to cater for those use cases.
Though all hope is not lost, You can easily explicitly add other Lombok specific annotations to individually cater for each of those options.
The good news is, the implementation of these other Lombok specific annotations takes precedence over @Data
annotation.
For example, in addition to @Data
annotation, you could add :-
@Setter(AccessLevel.NONE)
annotation. To make the class read only.@Getter(AccessLevel.NONE)
annotation. To make the class write-only.@AllArgsConstructor
annotation. To have an "all args constructor" instead of a "required args constructor".@ToString(includeFieldNames=false)
annotation. To inform Lombok not to display non-static field names and simply return a list of all non-static field values separated by commas.- etc...
As you can see, this makes @Data
annotation more flexible to specific requirements.
Let's create a Person
class to demonstrate this behaviour. Full code here
//demo3
package com.stevenmwesigwa.data.demo3;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.ToString;
@AllArgsConstructor
@ToString(includeFieldNames = false)
@Data
public class Person {
private String firstname;
private String lastname;
}
On top of what @Data
annotation does, @AllArgsConstructor
annotation provides us with an "all args constructor" and @ToString(includeFieldNames = false)
annotation provides us with a string representation of our object that lucks 'field names'.
Feel free to play with other options you may have at your disposal.
# Make Lombok Generate A Private Constructor And A Public Static Factory Method
There're cases when you don't want your client to have direct access to call your constructor.
For a case like this, You normally want to make the constructor private
and provide a public static
factory method wrapper to the client so that they're still able to set field values.
This can be achieved with @AllArgsConstructor(staticName="of")
or @RequiredArgsConstructor(staticName="of")
annotation depending on your use case.
This is cool and fine, though you probably should keep in mind that we're able to achieve the same result by passing a staticConstructor
parameter to @Data
annotation.
i.e @Data(staticConstructor="of").
NOTE: You're free to provide your own "staticConstructor" value, though a lot of developers prefer to use "of".
Let's create a Person
class to demonstrate this behaviour. Full code here
//demo4
package com.stevenmwesigwa.data.demo4;
import lombok.Data;
@Data(staticConstructor="of")
public class Person {
private String firstname;
private String lastname;
}
With the setup above, the client can easily create a new instance of the class like this Person.of()
When we create and run an App.java
class that makes use of this Person class Full code here
, we have successfully returned a string representation of our Person object. i.e Person(firstname=Steven, lastname=Mwesigwa)
I hope you learn a thing or two from this blog. Thanks for checking it out.
If anything is unclear or you wish to make any corrections, don't hesitate to leave a comment. Your feedback is greatly appreciated.
Sharing is caring. Share this blog to help out others getting started with 'project Lombok' library for Java applications.
Au revoir!