Java Records Explained
December 23, 2025Introduced in Java 14 as a preview feature and finalized in Java 16, Records are a game-changer for writing cleaner, more concise Java code. They provide a compact syntax for declaring classes that are transparent holders for shallowly immutable data.
The Problem with POJOs
Before Records, creating a simple data carrier class (POJO) required a lot of boilerplate:
public class Person {
private final String name;
private final int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
@Override
public boolean equals(Object o) { /* ... */ }
@Override
public int hashCode() { /* ... */ }
@Override
public String toString() { /* ... */ }
}
The Record Solution
With Records, the same class can be defined in a single line:
public record Person(String name, int age) {}
What You Get for Free
When you define a record, the compiler automatically generates:
- Private final fields for all components.
- Public accessor methods (e.g.,
name()andage()). Note: nogetprefix! - Canonical Constructor with the same arguments as the record header.
equals()andhashCode()implementations.toString()method that prints the record's name and its components.
Customizing Records
You can still customize records if needed. For example, validating data in the constructor:
public record Person(String name, int age) {
// Compact constructor
public Person {
if (age < 0) {
throw new IllegalArgumentException("Age cannot be negative");
}
}
}
When to Use Records
- DTOs (Data Transfer Objects): Perfect for carrying data between layers.
- Map Keys: Since
equalsandhashCodeare automatically reliable. - Stream Processing: ideal for temporary data structures in streams.
Records are immutable by default, making them thread-safe and less error-prone. They signal to other developers that "this class is just data."