Lesson 19: Records (record Keyword for Immutable Data Classes) – Java 16+

Java 16 introduced Records, a new way to create immutable data classes with minimal boilerplate. They are perfect for storing data and replacing POJOs (Plain Old Java Objects) that require a lot of code for simple tasks.


1. What Are Records?

Records are immutable data classes.
Automatically generate equals(), hashCode(), and toString().
Eliminate boilerplate code for simple objects.


2. How to Define a Record?

📌 Syntax of a Record

record Person(String name, int age) { }

This automatically creates:

  • Private final fields (name, age)
  • A constructor
  • Getters (name() and age())
  • toString(), equals(), and hashCode() methods

3. Example: Using a Record Instead of a Class

🔴 Traditional Java Class (Before Java 16)

public class Person {
    private final String name;
    private final int age;

    public Person(String name, int age) {  // Constructor
        this.name = name;
        this.age = age;
    }

    public String getName() { return name; }  // Getter
    public int getAge() { return age; }

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

🔴 Total Lines: ~15


🟢 Java 16+ Record (Same Functionality in 1 Line!)

record Person(String name, int age) { }

Automatically generates everything needed!
Cleaner and easier to maintain.


4. Records Provide Automatic Methods

📌 Example: Using a Record in Java 16+

public class Main {
    public static void main(String[] args) {
        Person p = new Person("Alice", 25);

        System.out.println(p.name());   // ✅ Getter
        System.out.println(p.age());    // ✅ Getter
        System.out.println(p);          // ✅ Auto-generated toString()
    }
}

Output:

Alice
25
Person[name=Alice, age=25]

No need to manually write getName(), getAge(), or toString().


5. Records Are Immutable

🚨 You cannot modify record fields after creation!

📌 Example: Record Fields Are final

record Car(String model, int year) { }

public class Main {
    public static void main(String[] args) {
        Car car = new Car("Tesla", 2022);
        car.year = 2023; // ❌ ERROR: Cannot modify a record field!
    }
}

Compiler Error:

error: cannot assign a value to final variable 'year'

Records are immutable by default!


6. Adding Custom Methods to a Record

📌 Example: Adding a Custom Method

record Rectangle(double width, double height) {
    double area() {  // ✅ Custom method
        return width * height;
    }
}

Records can have methods, but fields remain immutable!


7. When to Use Records?

🚀 Use record when:
✔ You need an immutable data holder.
✔ You want auto-generated constructors, getters, and toString().
✔ You are working with DTOs (Data Transfer Objects), API responses, or simple models.

🚨 Do NOT use record when:
❌ You need mutable objects (Use a normal class instead).
❌ You need inheritance (Records cannot extend other classes).


8. Comparison: Traditional Class vs. Record

FeatureTraditional ClassJava 16+ Record
Lines of Code🔴 15+ lines🟢 1 line
Getters Required?✅ Yes (getName(), getAge())❌ No (name(), age())
toString() Required?✅ Yes❌ No (Auto-generated)
Immutability❌ Optional✅ Always Immutable

Lesson Reflection

  1. How do Records simplify Java development compared to traditional classes?
  2. Why are Records immutable by default?
  3. When should you use a Record instead of a regular class?

Answers to Reflection Questions on Java 16+ Records (record)


1️⃣ How do Records simplify Java development compared to traditional classes?

Records eliminate boilerplate code

  • No need to manually write getters, constructors, toString(), equals(), or hashCode().
  • This makes code cleaner, more readable, and easier to maintain.

📌 Example: Traditional Class vs. Record

🔴 Before Java 16 (Verbose Code)

public class Person {
    private final String name;
    private final int age;

    public Person(String name, int age) {  // Constructor
        this.name = name;
        this.age = age;
    }

    public String getName() { return name; }
    public int getAge() { return age; }

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

🟢 Java 16+ Record (Same Functionality in 1 Line!)

record Person(String name, int age) { }

Less code, same result!


2️⃣ Why are Records immutable by default?

Records are meant to be simple, read-only data holders.
Immutable objects are safer, prevent unintended modifications, and work well in multi-threaded applications.

📌 Example: Trying to Modify a Record Field

record Car(String model, int year) { }

public class Main {
    public static void main(String[] args) {
        Car car = new Car("Tesla", 2022);
        car.year = 2023; // ❌ ERROR: Cannot modify a record field!
    }
}

🚨 Compiler Error:

error: cannot assign a value to final variable 'year'

Since fields are final, records cannot be modified after creation.


3️⃣ When should you use a Record instead of a regular class?

🚀 Use a record when:
✔ You need an immutable data holder.
✔ You want automatic getters and methods (toString(), equals(), etc.).
✔ You are working with DTOs (Data Transfer Objects), API responses, or simple models.

🚨 Do NOT use a record when:
❌ You need mutable objects (Use a normal class instead).
❌ You need inheritance (Records cannot extend other classes).

📌 Example: When to Use a Record (Good Use Case)

record User(String username, String email) { }

Perfect for API responses, where data should not change.

📌 Example: When NOT to Use a Record (Bad Use Case)

record BankAccount(String accountNumber, double balance) { 

    void deposit(double amount) { 
        balance += amount; // ❌ ERROR: Cannot modify record field!
    }
}

🚨 Records cannot have mutable fields, so BankAccount should be a regular class.


🔍 Key Takeaways

Records simplify Java development by reducing boilerplate code.
They are immutable by default, making them safer and more reliable.
Use Records for immutable data structures like DTOs and API responses.

Making POJOs Simpler: New Ways to Reduce Boilerplate Code

While Records (record) simplify immutable data classes, Java does not provide a built-in solution for mutable POJOs with get() and set() methods. However, there are modern approaches that can reduce boilerplate code in traditional JavaBeans (POJOs).


Lombok is a Java library that automatically generates get(), set(), toString(), equals(), and hashCode() methods using annotations.

📌 Example: Simplifying a POJO with Lombok

import lombok.Data;

@Data  // Generates getters, setters, toString(), equals(), and hashCode()
public class Person {
    private String name;
    private int age;
}

Equivalent to this manually written Java class:

public class Person {
    private String name;
    private int age;

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }

    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }

    @Override
    public boolean equals(Object o) { /* Auto-generated */ }
    @Override
    public int hashCode() { /* Auto-generated */ }
}

Lombok reduces POJO boilerplate from 50+ lines to just 5!
Requires Lombok dependency (Maven/Gradle).

📌 Lombok Dependency for Maven

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <scope>provided</scope>
</dependency>

2️⃣ Java Records (record) – Best for Immutable POJOs

If your class doesn’t need setters, you can use Java 16+ Records instead.

📌 Example: Replacing POJOs with a Record

record Person(String name, int age) { }

Automatically generates getters, toString(), equals(), and hashCode().
Immutable by default (no set() methods).
🚨 Use Records only if you don’t need mutable fields!


3️⃣ Java 21: New value Keyword (Experimental – Future Feature)

In Java 21+, the value keyword (preview feature) is being introduced for lightweight, immutable data classes.

📌 Future Example: Using value Classes (Java 21+)

value class Person {
    String name;
    int age;
}

🚀 This is still an experimental feature, but it may provide a built-in way to reduce POJO boilerplate without Lombok.


🔍 Summary: Best Approach for Your Use Case

Use CaseBest ApproachWhy?
Immutable POJOs (No set())record (Java 16+)No setters, less boilerplate
Mutable POJOs (With set())Lombok @DataAuto-generates getters & setters
Future-Proof Lightweight ClassesJava 21+ value class (Experimental)Will reduce POJO boilerplate further

🚀 Final Thoughts

Use Lombok (@Data) if you need setters and mutable objects.
Use record (Java 16+) for immutable data classes.
Java 21+ may introduce a value keyword to simplify POJOs further.

The next Java 17+ feature: Pattern Matching in switch Statements 😊🚀

Java Sleep