Skip to main content

Command Palette

Search for a command to run...

Records: An Elegant Way to Transfer Data in Java

Updated
4 min read
Records: An Elegant Way to Transfer Data in Java

With the introduction of Java 14, one of the most prominent features was "Records." Records offer a new approach to modeling classes as bearers of immutable data. Traditionally, building a basic data transfer object (DTO) in Java required a lot of boilerplate code. Records considerably decrease this burden and provide an intuitive way to encapsulate and transport data.

In this article, we will explore what Records are, how they work, why to use them, and most importantly, when to use them.

What are Java Records?

Records are a special kind of classes designed mainly to hold immutable data. It automatically generate the necessary methods for basic data handling, which helps reduces the amount of boiler plate commonly associated with plain java objects, such as constructors, getters, toString(), hashCode(), and equals() methods. By default Records in java are immutable, once created the data it holds cannot be changed.

Syntax

Let's create a Book class in the traditional Java way. Our class Book should have a title, a price, an ISBN and finally the authors name.

import java.util.Objects;

public class Book {
    private final String title;
    private final double price;
    private final String isbn;
    private final String authorName;

    public Book(String title, double price, String isbn, String authorName) {
        this.title = title;
        this.price = price;
        this.isbn = isbn;
        this.authorName = authorName;
    }

    public String getTitle() {
        return title;
    }

    public double getPrice() {
        return price;
    }

    public String getIsbn() {
        return isbn;
    }

    public String getAuthorName() {
        return authorName;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Book book = (Book) o;
        return Double.compare(book.price, price) == 0 &&
                title.equals(book.title) &&
                isbn.equals(book.isbn) &&
                authorName.equals(book.authorName);
    }

    @Override
    public int hashCode() {
        return Objects.hash(title, price, isbn, authorName);
    }

    @Override
    public String toString() {
        return "Book{" +
                "title='" + title + '\'' +
                ", price=" + price +
                ", isbn='" + isbn + '\'' +
                ", authorName='" + authorName + '\'' +
                '}';
    }
}

Phew ! that's a lot of code lines for such a simple class.

NB: We use the final keyword in this example to ensure the immutablity.

Let's try to do the same but this time with records

public record Book(String title, double price, String isbn, String authorName) {}

And yes, it's as simple as that. Java will handle generating the constructor, the getters, the hash, equals, and toString methods. No setters are needed because records are immutable.

Why Use Records?

  • Immutability by Design : Records are immutable, which makes them ideal for thread safe data transfer.

  • Reduced Boilerplate Code: Records automatically generate the data handling methods, drastically reducing the boilerplate code, making the classes easier to maintain and read.

Some use cases

  • Data Transfer Objects(DTOs) : DTOs are simple objects that move data between different parts of an application, like in REST APIs, service layers, or database queries. Records are great for this because of their built in immutability.

  • Return Types for Methods: Record can be used to bundle values from multiple related data source.This eliminates the need for verbose classes or cumbersome return types like maps or arrays.

Records and data validation

Although Records automatically generate constructors and methods, you can still customize their behavior if needed. For example, you can define additional methods inside a Record or provide custom validation logic.

For example, a Book cannot have a negative price. Fortunately, Java has thought of this and introduced the canonical constructor.

public record Book(String title, double price, String isbn, String authorName) {

    // Canonical constructor
    public Book {
        if (price < 0) {
            throw new IllegalArgumentException("Price cannot be negative");
        }
    }
}

If we take a look at the generated code, the Book.class file we'll find this constructor.

public Book(String title, double price, String isbn, String authorName) {
            if (price < 0.0) {
                throw new IllegalArgumentException("Price cannot be negative");
            } else {
                this.title = title;
                this.price = price;
                this.isbn = isbn;
                this.authorName = authorName;
            }
        }

The code we put in the canonical constructor was inserted into the generated constructor.

Limitations

While Records are useful, they are not suitable for all situations and have some limitations :

  • Immutability : Records cannot be used if you need mutable data structures like a database entity. In that case a traditional java class would be more appropriate

  • No Inheritance: Records can implement interfaces; however, they cannot extend other classes because they already extend the Record class. We can see that if we inspect the bytecode using the javap -c command.

      public final class Book extends java.lang.Record {
        private final java.lang.String title;
      // the rest is hidden
    

Conclusion

Java Records offer a simple and modern way to handle data by reducing boilerplate code, ensuring immutability, and making code easier to read. They come with built-in equals(), hashCode(), and toString() methods, making them great for data transfer objects, method return types, and configuration classes.

Although they have some limitations, their ease of use and power make them a useful tool for Java developers. As Java evolves, Records will likely become a common pattern for managing simple, unchangeable data structures, helping us developers write cleaner and more maintainable code.

71 views

More from this blog

Loukmane's articles

9 posts

Software Developer @MyTower. Crafting clean code by day, iced coffee addict by choice. Casual gamer and chess enjoyer ♟️💻☕. Always learning, always coding.