CodeGym /Courses /JAVA 25 SELF /Immutability — immutability of record classes

Immutability — immutability of record classes

JAVA 25 SELF
Level 22 , Lesson 1
Available

1. Record classes and immutability

Immutability is a property of an object where its state cannot be changed after creation. In other words: once an object is created, you cannot change its internal data. That’s it — set in stone.

Imagine a train ticket. As soon as it is printed, you cannot change the date or departure location (unless you resort to some Photoshop trickery). A ticket is an immutable object. If you want a different ticket — you buy a new one.

In programming, such objects are called immutable objects. They protect a program from accidental changes and make code safer and more predictable.

Characteristics of an immutable object

  • All fields of the object are final (they can be assigned only once, typically in the constructor).
  • No setters (methods that change field values).
  • All methods that return internal data either return copies or the data itself is also immutable.

Record classes in Java were designed specifically to create immutable data structures simply and painlessly.

Why is a record immutable?

  • All record components are final.
    When you declare a record, the compiler automatically makes all its fields private final. This means that after creating an object you will not be able to change its fields.
  • No setters.
    In a record class you cannot add a method setX(int x) — the compiler will not allow you to change a field’s value after the object is created.
  • The constructor assigns values only once.
    All values are set only at the time the object is created.

Example

public record Point(int x, int y) {}

Point p = new Point(5, 10);
// p.x = 7; // Compilation error: field x has private access and is final
// p.x(7);  // Error: no setter!
System.out.println(p.x()); // 5

An attempt to modify a field or call a non-existent setter leads to a compilation error. Java strictly enforces the immutability contract.

2. Advantages of immutable objects

Safety in multithreading

In multithreaded programs (which are most of them nowadays!) immutable objects are like body armor. If an object cannot be changed, different threads can safely read it without fear that someone is changing the data at the same time. There is no need to synchronize access or worry about data races.

Fact: many classes in the Java standard library that are actively used in multithreaded scenarios are either immutable or specially protected from modification.

Easier to reason about code

If an object is immutable, you can always be sure: you pass it to another method or class — and it won’t change “behind your back.” This greatly simplifies reading and debugging code. No need to guess who might have changed a field — nobody could!

Convenient as keys in collections

Immutable objects are great as keys in collections like HashMap or HashSet. Why? Because their equals and hashCode depend only on fields that don’t change. Therefore, the object won’t “get lost” in a collection because its state changed.

Fewer hidden bugs

A mutable object is easy to ruin by accidentally passing a reference somewhere. An immutable object is like a printed book: nobody can tear out or rewrite a page.

3. Comparison with regular classes

Let’s compare the behaviour of a regular class and a record class. As an example, we’ll take a simple model of a point on a plane.

Regular class (mutable)

public class PointClass {
    private int x;
    private int y;

    public PointClass(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() { 
        return x; 
    }
    public int getY() { 
        return y; 
    }

    public void setX(int x) { 
        this.x = x; 
    }
    public void setY(int y) { 
        this.y = y; 
    }
}

You can create an object and then change its state as much as you like:

PointClass p = new PointClass(1, 2);
p.setX(10); // p.x is now 10

Record class (immutable)

public record Point(int x, int y) {}

You create an object — and that’s it, it stays the way it was created:

Point p = new Point(1, 2);
// p.x = 10;   // Error! No access to the field
// p.x(10);    // Error! No setter

Table: behavior comparison

Regular class Record class
Fields any only private final
Setters can be added cannot be added
Mutability mutable immutable
Auto-generation no yes (equals, hashCode, toString)

4. Practice: how to use record immutability

Let’s try a small example with a record class. Suppose we have a banking application, and we want to store information about a transaction:

public record Transaction(String fromAccount, String toAccount, double amount) {}

Create an object:

Transaction t = new Transaction("12345", "67890", 1500.0);
System.out.println(t);
// Transaction[fromAccount=12345, toAccount=67890, amount=1500.0]

Let’s try to “transfer” the money to a different account:

// t.toAccount = "11111"; // Error! The field is final; no access
// t.toAccount("11111");  // Error! No setter

If we need a different transaction — we create a new object:

Transaction t2 = new Transaction(t.fromAccount(), "11111", t.amount());

Important: immutability does not mean “inconvenient.” It’s just a different style of work: if you need a new state — create a new object.

5. Immutability caveats: what to remember

Immutability is not always absolute!

A record class guarantees that its fields won’t change. But if a field is a reference to a mutable object (for example, an array or a regular class), then the contents of that object can be changed.

Example with an array

public record DataHolder(int[] data) {}

int[] arr = {1, 2, 3};
DataHolder holder = new DataHolder(arr);
arr[0] = 99;
System.out.println(holder.data()[0]); // 99! The array changed

Takeaway: if you want true immutability, use only immutable types (String, Integer, other records, etc.) or make copies of mutable objects inside the canonical constructor of the record class. For example:

int[] copy = Arrays.copyOf(data, data.length);

6. How to make a regular class immutable

If you want to make a regular class immutable, you’ll have to do it manually:

  • Mark all fields as private final,
  • Do not add setters,
  • Initialize all fields only via the constructor,
  • If a field is a mutable object, make a defensive copy.

Example

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

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

You have to admit, with a record class this is simpler and shorter:

public record User(String name, int age) {}

7. Common mistakes when working with immutable record classes

Mistake #1: trying to modify a field after creation.
Beginners often try to write p.x = 42; or p.x(42); for a record object. But the compiler immediately says: “Not allowed! The field is final, and there is no setter.”

Mistake #2: using mutable objects as record components.
If you add to a record a field of type List, Map, an array, or another mutable object, then the record itself won’t protect you from changes to that object’s contents. For example, if you have a record User(List<String> hobbies), someone can add or remove an element from the list, and that will change the state of your record object. To avoid this, use immutable collections (List.copyOf, Collections.unmodifiableList) or make copies of collections inside the record’s constructor.

Mistake #3: misunderstanding immutability.
Some people think that if an object is a record, it is protected from any changes. In reality, if the fields are references to mutable objects, their contents can be changed, and that can lead to unexpected bugs.

1
Task
JAVA 25 SELF, level 22, lesson 1
Locked
Personal Library Entry 📖
Personal Library Entry 📖
1
Task
JAVA 25 SELF, level 22, lesson 1
Locked
Historical Figures Data 🗿
Historical Figures Data 🗿
1
Task
JAVA 25 SELF, level 22, lesson 1
Locked
Moving an object on the game map 🎮
Moving an object on the game map 🎮
1
Task
JAVA 25 SELF, level 22, lesson 1
Locked
Sensor Readings Tracking 📊
Sensor Readings Tracking 📊
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION