Immutable Records

Helpful immutable data carrier classes

Posted by Vipin Sharma on Tuesday, April 13, 2021 Tags: java JDK16   5 minute read

This is a draft post, work in progress:

When a Java object is not immutable, any thread in the application can change its state. Without correctly synchronizing code, some threads might not see the updated state of the object. Happens before order in Java memory model specifies when exactly some action is going to be visible to other action. In other words, happens before order specifies when exactly some updates made by one thread are going to be visible to other threads. This is one of the biggest problems professional java developers deal with every day. The immutable nature of the record eliminates this problem completely.

Part 1 of the post we saw record provides a way to create data carrier-classes without writing a lot of boilerplate code. As part of this post, we will focus on the Immutability feature of the record. Immutability is one of the best features provided by records. We can use record objects without worrying about any other threads changing its state.

Java language features making record immutable

Following are few ways the state of a record class can be changed if it behaves like the java class. Along with this we are also discussing what are record features that prevent it from making record immutable.

Index Way to update record java language features preventing it
1 Create a record sub-class and change the state of the sub-class. Record classes are implicitly final and can not be abstract, this way we can not create a sub-class of record class.
2 Extend some other class(superclass) and change the state of the superclass. All record classes extend java.lang.Record by default so can not extend any other class.
3 Add instance fields that can be modified. Record classes cannot declare instance fields.
4 Update record components. We can not assign a new value to record components as these are implicitly final. These are assigned values while initializing the record in canonical constructor.
5 Update record components in the constructor. Only canonical constructor can update record components. For other types of constructors assigning any of the record components in the constructor body results in a compile-time error.
6 Update record components using reflection. Record components have specific handling in Reflection Field API. This treatment is like hidden classes. You can read more about Hidden classes here.

Shallowly immutable

Record components are final, which means you can not change the field once assigned. Although we can change fields of the record component, there is no restriction on that, it makes the record shallowly immutable. Let’s see this with an example.

public class EmployeeTest {

    public static void main(String[] args) {
        List<Integer> integerList = new ArrayList<>();
        IntegerListRecord integerListRecord = new IntegerListRecord(integerList);



    record IntegerListRecord(List<Integer> integerList) {
        int getListSize()
            return integerList.size();

Here we created a list of integers(integerList), added one element into it, and initialized the record class with this. Calling method getListSize of record class results into 1. Now we add one more element in integerList and calling getListSize results into 2. Here we did not change record component (integerList) but updated the fields of the record component, which does not have any restriction. This is the reason we call the record shallowly immutable.


Records help you remove repetitive and error-prone code, and increases developer productivity. The immutability feature keeps it away from concurrency bugs. Using language features like this is going to make you a great developer everyone wants to hire.

If you want to get amazing Java jobs, I wrote an ebook 5 steps to Best Java Jobs. You can download this step-by-step guide for free!


  1. JEP 395
  3. Happens before order