Record Classes in Java
Learn Java records for creating concise immutable data-carrier classes.
1. What is a Record?
A record is a special kind of Java class designed primarily to model immutable data.
Records reduce the amount of boilerplate code required for classes that mainly store data.
equals(), hashCode(), and
toString().
2. Traditional Data Class
A normal data class can require many lines of repetitive code.
public final class Person {
private final String name;
private final int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String name() {
return name;
}
public int age() {
return age;
}
}
3. Basic Record Syntax
The same basic data model can be represented much more concisely with a record.
public record Person(
String name,
int age
) {
}
The components name and age become part of the
record's state.
4. Creating a Record Object
Person person =
new Person("Rohit", 25);
System.out.println(person.name());
System.out.println(person.age());
person.name(), rather than a traditional
getName() method.
5. Automatically Generated Constructor
A record automatically receives a canonical constructor corresponding to its declared components.
public record Employee(
String name,
int id
) {
}
Java effectively provides a constructor equivalent to:
new Employee(
"Samiksha",
101
);
6. Record Accessor Methods
Each record component automatically gets an accessor method with the same name as the component.
record Product(
String name,
double price
) {
}
Product product =
new Product("Laptop", 55000.0);
System.out.println(product.name());
System.out.println(product.price());
7. toString()
Records automatically provide a useful toString()
implementation containing the record name and component values.
record Student(
String name,
int marks
) {
}
Student student =
new Student("Sejl", 85);
System.out.println(student);
Student[name=Sejl, marks=85]
8. equals() and hashCode()
Records automatically implement value-based equals()
and hashCode() behavior using their components.
record Point(
int x,
int y
) {
}
Point first =
new Point(10, 20);
Point second =
new Point(10, 20);
System.out.println(
first.equals(second)
);
9. Records are Shallowly Immutable
Record components are final, so their references or primitive values cannot be reassigned after construction.
record Person(
String name,
int age
) {
}
Person person =
new Person("Samadhan", 22);
// person.age = 20;
// Invalid because record components cannot be reassigned.
10. Compact Constructor
A record can define a compact constructor to validate or normalize its component values.
public record Person(
String name,
int age
) {
public Person {
if (age < 0) {
throw new IllegalArgumentException(
"Age cannot be negative"
);
}
}
}
In a compact constructor, Java handles the assignment of constructor parameters to record components automatically.
11. Canonical Constructor
You can also explicitly declare the canonical constructor when you need complete control over its implementation.
public record Employee(
String name,
int salary
) {
public Employee(
String name,
int salary
) {
if (salary < 0) {
throw new IllegalArgumentException(
"Salary cannot be negative"
);
}
this.name = name;
this.salary = salary;
}
}
12. Custom Methods in Records
Records can contain custom instance methods just like normal classes.
public record Rectangle(
double width,
double height
) {
public double area() {
return width * height;
}
}
Rectangle rectangle =
new Rectangle(10, 5);
System.out.println(
rectangle.area()
);
13. Static Members
Records can also contain static fields and static methods.
public record User(
String username
) {
public static String type() {
return "Application User";
}
}
System.out.println(
User.type()
);
14. Record Restrictions
Records have several restrictions because they are specifically designed for data-centric modeling.
- A record cannot extend another class.
- A record is implicitly final.
- Record components cannot be reassigned.
- Instance fields cannot be declared independently of record components.
- Records can implement interfaces.
15. Records Implementing Interfaces
A record can implement one or more interfaces.
interface Printable {
void print();
}
record Employee(
String name,
int id
) implements Printable {
public void print() {
System.out.println(
name + " - " + id
);
}
}
Employee employee =
new Employee("Sejl", 101);
employee.print();
16. Record vs Class
| Record | Normal Class |
|---|---|
| Designed for data carriers | General-purpose object modeling |
| Compact syntax | More boilerplate may be required |
| Components are final | Fields can be mutable or immutable |
| Automatically provides accessors | Accessors must usually be written manually |
| Automatically provides value-oriented equals/hashCode | Must be implemented when required |
| Implicitly final | Can be designed for inheritance |
17. Records and Inheritance
Records cannot extend user-defined classes because every record already
extends java.lang.Record.
public record Person(
String name
) {
}
// A record cannot extend another class.
// public record Student(...) extends Person { }
However, records can implement interfaces.
18. Generic Records
Records can be generic, allowing them to represent different types of data.
public record Box<T>(
T value
) {
}
Box<String> text =
new Box<>("Java");
Box<Integer> number =
new Box<>(100);
System.out.println(text.value());
System.out.println(number.value());
19. Nested Records
Records can be declared inside other classes when they belong naturally to a larger domain model.
public class OrderService {
record Order(
int id,
double amount
) {
}
public static void main(String[] args) {
Order order =
new Order(101, 2500.0);
System.out.println(order);
}
}
20. Records as DTOs
Records are particularly useful for simple Data Transfer Objects, API request objects, response objects, and projection models.
public record UserResponse(
Long id,
String name,
String email
) {
}
A controller or service can return this record as a response model without creating repetitive getter and setter code.
21. Validation in Records
Compact constructors are useful for enforcing invariants.
public record Account(
String username,
String email
) {
public Account {
if (username == null ||
username.isBlank()) {
throw new IllegalArgumentException(
"Username is required"
);
}
if (email == null ||
email.isBlank()) {
throw new IllegalArgumentException(
"Email is required"
);
}
}
}
22. Example 🌍🫠
Consider a payment application that needs to transfer payment information between layers.
public record PaymentRequest(
String customerId,
double amount,
String currency
) {
public PaymentRequest {
if (amount <= 0) {
throw new IllegalArgumentException(
"Amount must be greater than zero"
);
}
}
}
PaymentRequest request =
new PaymentRequest(
"CUST-101",
2500.0,
"INR"
);
System.out.println(
request.customerId()
);
System.out.println(
request.amount()
);
System.out.println(
request.currency()
);
23. Records and Serialization
Records can be used with many Java libraries that work with Java objects, including JSON serialization libraries, provided the library supports records.
public record ProductResponse(
Long id,
String name,
double price
) {
}
This makes records convenient for API response models in modern Java applications.
24. Java Version
| Java Version | Record Status |
|---|---|
| Java 14 | First preview |
| Java 15 | Second preview |
| Java 16 | Standard feature |
25. Advantages of Records
- Very concise syntax.
- Less boilerplate code.
- Built-in accessors.
- Built-in equals and hashCode.
- Useful toString implementation.
- Good fit for immutable data carriers.
- Excellent for DTOs and API models.
- Can implement interfaces.
- Can contain validation and business-related helper methods.
26. Limitations of Records
- Cannot extend another class.
- Are implicitly final.
- Not suitable for highly mutable domain objects.
- Provide shallow rather than deep immutability.
- Should not be used merely because they are shorter.
27. Best Practices
- Use records for data-centric models.
- Use compact constructors for validation.
- Keep record methods focused and simple.
- Use normal classes when the object requires mutable state or inheritance.
- Use records for DTOs when the application architecture benefits from immutable data.
- Remember that referenced mutable objects are not automatically deeply immutable.
28. Interview Questions
java.lang.Record.
Summary
In this lesson, you learned:
- What Java records are
- Record syntax
- Automatic constructors and accessors
- equals(), hashCode(), and toString()
- Shallow immutability
- Compact constructors
- Custom methods and static members
- Records implementing interfaces
- Generic records
- Records as DTOs
- Validation inside records
- Advantages and limitations