Writing DTOs With Java8, Lombok, and Java14+ - The Legend of Hanuman

Writing DTOs With Java8, Lombok, and Java14+


In this article, I will try to help you understand how updated versions of Java and using the right set of libraries can reduce the development efforts and boilerplate code. In organizations, we tend to use one version for a longer period of time as upgrading versions of software is not quick and requires extensive testing.

However, because of this thought, we generally miss using new features available with new versions as software providers continuously update their features.  

Let’s take the example of DTOs writing (Data transfer object). In Java, to store data that will be passed across different layers, we create data objects called DTOs. Writing a correct DTO involves including the following elements in a class.

  1. Data field definition 
  2. Getters and setters 
  3. Parameterized constructors
  4. If building a complex object, then using a builder pattern
  5. Writing toString method 
  6. Override equals and hashcode

Now, consider all the above points and see how DTOs were being written and now, how they can be achieved with less code using libraries and Java 14+.

Using Plain Java Class ( Before Java 14 and Lombok)

import java.util.Objects;

/**
 * Represents the details of an employee.
 */
public class EmployeeDetail {
    private String employeeId;
    private String employeeName;
    private String employeeAddress;

    /**
     * Constructs an EmployeeDetail with the specified ID and name.
     *
     * @param employeeId the ID of the employee
     * @param employeeName the name of the employee
     */
    public EmployeeDetail(String employeeId, String employeeName) {
        this.employeeId = employeeId;
        this.employeeName = employeeName;
    }

    /**
     * Sets the address of the employee.
     *
     * @param employeeAddress the address of the employee
     * @return the updated EmployeeDetail object
     */
    public EmployeeDetail withAddress(String employeeAddress) {
        this.employeeAddress = employeeAddress;
        return this;
    }

    /**
     * Returns the ID of the employee.
     *
     * @return the employee ID
     */
    public String getEmployeeId() {
        return employeeId;
    }

    /**
     * Returns the name of the employee.
     *
     * @return the employee name
     */
    public String getEmployeeName() {
        return employeeName;
    }

    /**
     * Returns the address of the employee.
     *
     * @return the employee address
     */
    public String getEmployeeAddress() {
        return employeeAddress;
    }

    /**
     * Indicates whether some other object is "equal to" this one.
     *
     * @param o the reference object with which to compare
     * @return true if this object is the same as the obj argument; false otherwise
     */
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        EmployeeDetail that = (EmployeeDetail) o;
        return Objects.equals(employeeId, that.employeeId) &&
               Objects.equals(employeeName, that.employeeName) &&
               Objects.equals(employeeAddress, that.employeeAddress);
    }

    /**
     * Returns a hash code value for the object.
     *
     * @return a hash code value for this object
     */
    @Override
    public int hashCode() {
        return Objects.hash(employeeId, employeeName, employeeAddress);
    }

    /**
     * Returns a string representation of the object.
     *
     * @return a string representation of the object
     */
    @Override
    public String toString() {
        return "EmployeeDetail{" +
               "employeeId='" + employeeId + '\'' +
               ", employeeName="" + employeeName + "\'' +
               ", employeeAddress="" + employeeAddress + "\'' +
               '}';
    }
}

Here, you can see you are almost writing everything starting from fields, constructors, getter/setters and builder objects, toString, and hashcode. 

Since the Lombok library came into existence, this code has been reduced to a few lines as it provides many annotations, which helps developers reduce writing boilerplate code like getter/setter, constructors, tostring, etc. Take the following, for example.

Using Lombok Library

import lombok.*;

@Getter
@Setter
@NoArgsConstructor
@ToString
@EqualsAndHashCode
public class EmployeeDetail {
    private String employeeId;
    private String employeeName;
    private String employeeAddress;

    public EmployeeDetail(String employeeId, String employeeName, String employeeAddress) {
        this.employeeId = employeeId;
        this.employeeName = employeeName;
        this.employeeAddress = employeeAddress;
    }
}

Now, the code has drastically been reduced, and you are only focusing on defining required fields, and the rest of the things are taken care of by Lombok. You just need to use the correct annotation, and Libraries will do the rest of the work. After compiling the code, it will generate all required methods in the generated class.

I used Lombok for some time to reduce boilerplate code. However, I was happily surprised to see Record, which was introduced in Java 14 and higher versions. Currently, I am using Java 21, and below is the code snippet for writing an equivalent class as DTO (mentioned in the first example).

Using Java 21: Record Class

/**
 * Represents the details of an employee.
 */
public record EmployeeDetail(String employeeId, String employeeName, String employeeAddress) {
}

Isn’t this cool? You just mentioned your data fields in one line, and JDK takes care of the rest of the information. Here, the record class will automatically generate the constructor, toString(), equals, and hashcode methods for you, and you don’t need to write anything. 

Now, assume you need to instantiate Record and get the data, you simply need to do the following. 

public class Main {
    public static void main(String[] args) {
        EmployeeDetail employee = new EmployeeDetail("12345", "Nitesh Gupta", "XYZ");

        // Accessing the data
        String id = employee.employeeId();
        String name = employee.employeeName();
        String address = employee.employeeAddress();       
    }
}

Conclusion

My suggestion would be to start upgrading your apps with the latest Java versions, utilize the features available to reduce development effort and write clean code. 

Happy coding!


Share this content:

I am a passionate blogger with extensive experience in web design. As a seasoned YouTube SEO expert, I have helped numerous creators optimize their content for maximum visibility.

Leave a Comment