Records in Java: Simplifying Data Classes

Records, introduced as a preview feature in Java 14 and officially released in version 16, are a powerful tool designed to simplify the creation of classes that primarily hold data. They significantly reduce boilerplate code by automatically generating commonly used methods like equals, hashCode, and toString. Ideal for creating immutable data carriers with minimal effort, records make your code more concise and easier to maintain.

Records are a new feature in Java (introduced in Java 14 as a preview-feature) that help reduce boilerplate code for classes that primarily hold data. They are ideal for creating immutable data carriers with minimal effort.

Why Use Records?

Before records, creating a simple data class required a lot of boilerplate code. For example, consider a Favorites class with two fields:

public class Favorites {
    private final String name;
    private final int rating;

    public Favorites(String name, int rating) {
        this.name = name;
        this.rating = rating;
    }

    public String getName() { return name; }
    public int getRating() { return rating; }

    @Override
    public String toString() {
        return "Favorites[name=" + name + ", rating=" + rating + "]";
    }

    @Override
    public boolean equals(Object o) { /*...*/ }
    @Override
    public int hashCode() { /*...*/ }
}

This is a lot of code for a simple data class. Before records, developers often used libraries like Lombok, which generates getters, setters, equals, and hashCode methods directly in the compiled class files, without adding boilerplate code to the source.

For example, with Lombok, the above class could be simplified to:

import lombok.Data;

@Data
public class Favorites {
    private final String name;
    private final int rating;
}

However, Lombok is an external dependency, and until records were introduced, Java did not have a built-in way to achieve this level of simplicity.

How Records Work

A record is a special kind of class that automatically generates:

Here’s how you can define the same Favorites class as a record:

public record Favorites(String name, int rating) {}

That’s it! This single line replaces the entire Favorites class above.

Example Usage

public class Main {
    public static void main(String[] args) {
        Favorites favorite = new Favorites("JBerries", 10);
        System.out.println(favorite.name()); // Access fields directly
        System.out.println(favorite.rating()); // Access fields directly
        System.out.println(favorite); // Automatically uses toString()
    }
}

Output:

JBerries
10
Favorites[name=JBerries, rating=10]

Key Features of Records

  1. Immutability: Records are immutable by default. Fields cannot be modified after creation.
  2. No Setters: Records do not generate setter methods.
  3. Compact Constructors: You can define a compact constructor for validation or additional logic.
public record Favorites(String name, int rating) {
    public Favorites {
        if (rating < 0 || rating > 5) {
            throw new IllegalArgumentException("Rating must be between 0 and 5");
        }
    }
}
  1. Static Fields and Methods: You can add static fields and methods to records.
public record Favorites(String name, int rating) {
    public static final String DEFAULT_NAME = "Unknown";

    public static void printMessage() {
        System.out.println("Hello from Favorites record!");
    }
}
  1. Interfaces: Records can implement interfaces.
public record Favorites(String name, int rating) implements Runnable {
    @Override
    public void run() {
        System.out.println(name + " is running with a rating of " + rating);
    }
}

Limitations of Records

When to Use Records

Use records when:

Avoid records when:

Records are a powerful addition to Java, making it easier to create data-centric classes. They eliminate boilerplate code and enforce immutability, making your code cleaner and more maintainable. Before records, developers relied on tools like Lombok to generate getters, setters, and other boilerplate code, but now Java provides a built-in solution.