The Ultimate Guide for Records in Java 21
Bruno Monteiro
Senior Software Developer / Engineer | Java | Spring | Go / Golang | AWS | GCP | Microsoft Azure
Java Records, introduced in Java 17, simplify data encapsulation by eliminating boilerplate code in immutable data structures. If you've ever written a POJO (Plain Old Java Object) with multiple fields, constructors, getters, and implementations for equals(), hashCode() and toString(), you know how tedious and repetitive it can be.
Records address this by automatically generating these methods while ensuring immutability. But how exactly do they work, how can they be used effectively, and what rules should you be aware of? Let's break it down.
This article is based on OCP Oracle Certified Professional Java SE 21 Developer Study, specifically Chapter 7: Beyond Classes – Encapsulating Data with Records.
Context: Immutable Object Pattern
The Immutable Object Pattern is a design principle in object-oriented programming where an object's state cannot be modified after it is created. This pattern is widely used in Java to provide thread safety, simplify code maintenance, and prevent unintended side effects. Immutable objects eliminate issues related to concurrent modifications since their internal state remains unchanged throughout their lifetime.
Java provides built-in support for immutability through common classes like String, Integer, and LocalDate. When designing custom immutable objects, developers typically mark fields as private final, avoid setter methods, and make sure that mutable fields (such as collections) are copied rather than directly referenced. This approach enforces a predictable and stable object state.
Why Use Records?
With records, you can achieve the same practical effects from the last snippet in a single line:
By default, Java Records provide:
Customizing Constructors
In Java, records provide an implicit constructor, but you can override it using a compact constructor. This allows you to validate input without explicitly reassigning fields or redefining parameters in the constructor signature:
Additionally, custom constructors can be defined, but they must always delegate to the primary constructor using this() as their first statement:
One important limitation is that record fields are immutable. While constructor parameters can be modified inside the constructor, the actual record fields cannot be reassigned. The following example would result in a compilation error:
Records and Pattern Matching
One of the most powerful yet complex aspects of records is their integration with pattern matching. As a quick recap, pattern matching allows checking an object's type and automatically binding it to a variable if the test succeeds, eliminating the need for explicit casting. If you want to know more about pattern matching, particularly in switch statements and expressions, check my previous article.
Records naturally complement pattern matching by simplifying object decomposition. Here are the key rules to keep in mind when using records with pattern matching:
For Rule 4, the type does not need to be an exact match. For example, since String implements CharSequence, pattern matching can work with a CharSequence pattern on a String record field.
领英推荐
Regarding Rule 5, you can use the var keyword when working with generics (or any type). This removes the need to define an explicit class at compile time, making it highly useful in certain cases:
Although var is often a good choice, when working with generics, you may also explore other approaches (though sticking to explicit types or var is generally recommended for clarity):
Nested Record Patterns
Records can contain other records, allowing structured decomposition in pattern matching, in a somewhat recursive fashion:
Pattern Matching in switch and Records
Records improve switch expressions with records by eliminating manual casting and boilerplate checks:
Usual rules are applied here:
Common Questions
?? Are Records Just Plain Java Classes?
Yes and no. Records are classes, but with built-in immutability and automatic method generation.
?? Can I Add Methods to a Record?
Yes! You can override methods, define instance methods, and even add nested classes.
?? Can Records Have Mutable Fields?
No. Fields are implicitly final, meaning once a record is created, its values cannot be modified.
?? Are Records Good for Every Use Case?
Records are best for data-holding classes (like DTOs) but not ideal for mutable objects or complex business logic.
Conclusion
Java Records are a powerful feature for simplifying immutable objects, eliminating unnecessary boilerplate, and improving pattern matching. If you're working with data-centric classes, DTOs, or API models, Records help make your code cleaner, safer, and more maintainable.
Beyond reducing verbosity, Records integrate quite well with pattern matching, enabling concise object decomposition. This eliminates the need for explicit casting, enhances readability, and allows safer, more expressive switch statements and instanceof conditions. By using pattern matching with records, you can write more declarative and less error-prone code, making complex conditionals more intuitive.
If you want immutable, self-contained objects that work efficiently with pattern matching, Records are an excellent choice! ??
Backend Engineer | Kotlin | Java | Spring Boot | JUnit | Docker | AWS
4 天前Reading this, I can't help but think how similar the intent is to Kotlin's Data Classes.
Useful tips
Senior QA Automation Engineer | SDET | Java | Selenium | Rest Assured | Robot Framework | Cypress | Appium
3 周Very helpful
Senior Software Engineer | Java | Spring | AWS | Angular | React | Docker | Fullstack Developer
3 周Awesome content
Fullstack Software Engineer | Node | Typescript | React | Next.js | AWS | Tailwind | Nest.js | TDD | Docker
3 周Nice content and approach Bruno Monteiro Thanks for sharing!