Java Records
java records immutable DTO compact constructor accessor methods POJO Spring Boot Java 16
Learning Objectives
After completing this lesson, you will be able to:
- Understand what Records are
- Learn why Records were introduced
- Compare Records with traditional POJOs
- Create immutable data classes using Records
- Use Constructors in Records
- Add Methods to Records
- Use Compact Constructors
- Understand Record Internals
- Use Records with Collections and Streams
- Use Records in Spring Boot Applications
- Learn Best Practices
- Prepare for Java Interview Questions
Introduction
One of the biggest problems in Java before Java 16 was writing boilerplate code.
Consider a simple Employee class.
public class Employee {private final int id;private final String name;private final double salary;public Employee(int id,String name,double salary){this.id = id;this.name = name;this.salary = salary;}public int getId(){return id;}public String getName(){return name;}public double getSalary(){return salary;}@Overridepublic boolean equals(Object obj){...}@Overridepublic int hashCode(){...}@Overridepublic String toString(){...}}
Almost 80% of the code is boilerplate.
Java 16 introduced Records to solve this problem.
A Record automatically generates:
- Constructor
- Getters (Accessor Methods)
- equals()
- hashCode()
- toString()
Records are ideal for immutable data carriers such as DTOs, API responses, configuration objects, and value objects.
Why Records?
Without Records
public class User {private final String username;private final String email;// Constructor// Getters// equals()// hashCode()// toString()}
With Records
public record User(String username,String email){ }
That's all.
Benefits
- Less Code
- Immutable
- Thread Safe
- Better Readability
- Cleaner APIs
What is a Record?
A Record is a special kind of class designed to model immutable data.
Syntax
public record Employee(int id,String name,double salary){}
Creating Record Objects
Employee employee =new Employee(101,"Rahul",75000);System.out.println(employee);
Output
Employee[id=101,name=Rahul,salary=75000.0]
Accessor Methods
Records automatically generate accessor methods.
Employee employee =new Employee(101,"John",90000);System.out.println(employee.id());System.out.println(employee.name());System.out.println(employee.salary());
Output
101John90000.0
Notice:
Accessor methods are:
employee.id()
NOT
employee.getId()
Auto Generated Methods
Java automatically generates
- Constructor
- Accessor Methods
- equals()
- hashCode()
- toString()
Equivalent Class
Employee Record↓Compiler↓Immutable Java Class
equals()
Employee e1 =new Employee(1,"Amit",50000);Employee e2 =new Employee(1,"Amit",50000);System.out.println(e1.equals(e2));
Output
true
hashCode()
Automatically generated.
System.out.println(employee.hashCode());
toString()
Automatically generated.
System.out.println(employee);
Output
Employee[id=101,name=Amit,salary=50000]
Custom Constructor
Records support constructors.
public record Employee(int id,String name){public Employee(int id,String name){this.id = id;this.name = name;}}
Compact Constructor
Preferred approach.
public record Employee(int id,String name){public Employee{if(id <= 0){throw new IllegalArgumentException("Invalid ID");}}}
The compiler automatically assigns fields after validation.
Adding Methods
Records can contain methods.
public record Employee(int id,String name){public String greeting(){return "Welcome " + name;}}
Usage
System.out.println(employee.greeting());
Static Members
Records support static fields and methods.
public record User(String username){public static String company(){return "TechLearningPro";}}
Nested Records
public class Company{public record Employee(int id,String name){}}
Record with Collections
public record Department(String name,List<Employee> employees){}
For deep immutability, use immutable collection implementations or defensive copies.
Records with Streams
employees.stream().filter(e -> e.salary() > 80000).forEach(System.out::println);
Record Internals
Compiler
public record Employee(int id,String name){}
Conceptually becomes
final class Employeeextends Record
Properties
- Final Class
- Final Fields
- Immutable
- Cannot Extend Another Class
Record Memory Diagram
JVM Heap│Employee Record┌──────────────┐│ id = 101 ││ name = Rahul │└──────────────┘
Record Restrictions
Records can:
- Implement interfaces
- Have methods
- Have constructors
- Have static members
Records cannot:
- Extend another class
- Declare mutable instance fields
- Add extra instance variables outside the record components
Record vs POJO
| Record | POJO |
|---|---|
| Immutable | Mutable |
| Less Boilerplate | More Boilerplate |
| Auto-generated Methods | Manual Methods |
| Final | Usually Non-final |
| Better for DTO | Better for Rich Domain Models |
Real-World Example: API Response
public record ApiResponse(int status,String message){}
Real-World Example: Spring Boot DTO
public record EmployeeDTO(Long id,String name,String department){}
Spring Boot 3 fully supports Records for request and response DTOs.
Real-World Example: Configuration
public record DatabaseConfig(String url,String username,String password){}
Best Practices
Use Records for DTOs
Ideal for:
- REST Responses
- Request Objects
- Events
- Configuration
- Value Objects
Keep Records Immutable
Avoid mutable objects inside records when possible.
Use Compact Constructors
Validate inputs without repeating assignments.
Prefer Records for Read-Only Data
If objects should not change after creation, Records are an excellent choice.
Use with Streams
Records integrate naturally with Streams and functional programming.
Common Mistakes
Trying to Modify Fields
Wrong
employee.name = "Rahul";
Records are immutable.
Using Getters
Wrong
employee.getName();
Correct
employee.name();
Extending a Record
Wrong
class Manager extends Employee
Records are implicitly final.
Using Records for JPA Entities
Avoid using Records as JPA entities because most JPA providers require mutable entities, a no-argument constructor, and proxy support.
Use Records for DTOs instead.
Assuming Deep Immutability
A record's fields are final, but if a field references a mutable object (for example, a List), the contents can still change unless you create immutable copies.
Hands-on Exercise
Create a Java program that:
- Creates a Student record.
- Creates an EmployeeDTO record.
- Uses accessor methods.
- Demonstrates equals().
- Demonstrates hashCode().
- Creates a compact constructor for validation.
- Adds a custom method.
- Uses records with Streams.
- Uses records inside a Spring Boot REST response.
- Demonstrates nested records.
Summary
Records, introduced in Java 16, provide a concise and immutable way to model data. They eliminate boilerplate by automatically generating constructors, accessors, equals(), hashCode(), and toString(). Records are ideal for DTOs, API models, configuration objects, and value objects, and they integrate seamlessly with Streams, Lambdas, and Spring Boot. They are not intended to replace every class but are an excellent choice for immutable data carriers.
Key Takeaways
- Records are immutable data carriers.
- Records dramatically reduce boilerplate code.
- The compiler generates constructors, accessors,
equals(),hashCode(), andtoString(). - Accessor methods use the component name (for example,
name()), not JavaBean getters. - Compact constructors are ideal for validation.
- Records can implement interfaces but cannot extend classes.
- Records are implicitly final.
- Prefer Records for DTOs and API models.
- Avoid using Records as JPA entities.
- Records work well with Streams, Lambdas, and modern Spring Boot applications.
Professional Interview Questions
1What are Records in Java?
Professional Answer
Records are a special kind of class introduced in Java 16 to model immutable data. They automatically generate constructors, accessor methods, equals(), hashCode(), and toString(), significantly reducing boilerplate code. Records are best suited for data transfer objects (DTOs), configuration classes, and other immutable value objects.
Follow-up Questions
- Which Java version introduced Records?
- Can Records have constructors and methods?
- Are Records immutable?
Interview Tip: Record = Immutable Data Class + Auto-Generated Methods.
2What is the difference between a Record and a POJO?
Professional Answer
A POJO is a regular Java class that can be mutable or immutable and typically requires manually writing constructors, getters, equals(), hashCode(), and toString(). A Record is a specialized, immutable data class that automatically generates these members, making it ideal for simple data carriers while reducing boilerplate.
Follow-up Questions
- When should you choose a Record?
- Can a Record replace every POJO?
Interview Tip: POJO → General-Purpose Class, Record → Immutable Data Carrier.
3Can Records be used as JPA Entities?
Professional Answer
In most cases, no. JPA entities are typically mutable, require a no-argument constructor, and rely on proxy-based mechanisms for lazy loading. Records are immutable, final, and lack a no-argument constructor by default, making them unsuitable as JPA entities. However, Records are an excellent choice for DTO projections, REST request/response models, and read-only views.
Follow-up Questions
- What should you use Records for in Spring Boot?
- Why do JPA providers require mutable entities?
Interview Tip: JPA Entity → Regular Class, DTO / API Response → Record.