Create Immutable Value Objects using Java Record Classes

This is an Introduction to Java Record Classes. We will learn to use Java records to create Immutable data objects, or Value Objects.

Overview

While writing code for applications, we often need to create immutable Java Beans, which are also called as Value Objects. Java provides a new type of class called as records to create shallowly immutable objects.

What are Immutable Data Objects/Value Objects

We create immutable data objects to transfer data between two components. The sole responsibility of an immutable data object or a value object is to hold values. Moreover, we can define state of such value objects only when we create them. Thus, once created the state of value objects can only be read and cannot be modified. That is why they are also called immutable data objects.

Example of Immutable data object

package com.amitph.spring.tutorials.students.web;

public class StudentDto {
  private final Long id;
  private final String firstName;
  private final String lastName;
  private final int year;

  public StudentDto(Long id, String firstName, 
      String lastName, int year) {
    this.id = id;
    this.firstName = firstName;
    this.lastName = lastName;
    this.year = year;
  }

  public Long getId() {
    return id;
  }

  public String getFirstName() {
    return firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public int getYear() {
    return year;
  }
}Code language: Java (java)

This class has members which are final and along with an all argument constructor. Also, it has getter methods for each of the fields. It may also override the toString(), hashCode(), and equals() methods when required. We can use this class to return Student objects from a controller or hold the Students records retrieved from database. Optionally, we an also mark this class as final, which will prevent other classes to inherit it.

The Java Record classes help to reduce a lot of boilerplate code in such classes. In the next section we will talk about the records.

Introduction to Java Record class

In order to create a value object class, we can mark it as record instead of class, and the instance of that class will be value objects by default. However, there are few more things apart from the record keyword, which we are going to see in this section.

A Record class is a base class of all the records. Java introduced the record class as a preview feature in JAVA SE 14 and also kept it in JAVA SE 15. That means, java may drop, change, or finalise the record feature in future releases.

By definition a record class is shallowly immutable, carrier for a fixed set of values called as record components. Hence, if we convert the Student class into a record, the members like id, first name, last name, and year will be called as components of student record.

Java has provided a special syntax for declaring record classes, where the record components are declared in the class header. A record class will always have a canonical constructor, which has the same visibility as that of the record or more; private and final fields for each record component; and getter methods for each record components.

Record Class Syntax

At the minimal a record class syntax looks like this

public record <MY_RECORD>(<DATATYPE> var1, 
    <DATATYPE> var2, ..){
}Code language: Java (java)

We have used the keyword record instead of class. Also the class headers contains a comma separated list of members and their data types.

Example of Record Class

Now, we will see an example of record class by rewriting the StudentDto class.

Before, we do that we need to make sure we are using JDK 14 or higher. Also, make sure you have set language level to include the ‘preview’ features.

Student Record

package com.amitph.spring.tutorials.students.web;

public record StudentDto(Long id, 
    String firstname, String lastName, 
    int year) {
}Code language: Java (java)

The class header defines all record components also known as members of the class.

Next, we will test our record class

@Test
public void testDtoHasCorrectData() {
  StudentDto studentDto = 
      new StudentDto(111L, "fName", "lName", 2023);

  assertEquals(Long.valueOf(111), studentDto.id());
  assertEquals("fName", studentDto.firstname());
  assertEquals("lName", studentDto.lastName());
  assertEquals(2023, studentDto.year());
}Code language: Java (java)

From the test, it is clear that we can instantiate the class by providing the constructor arguments. Also we tested that all of the access methods return the fields values correctly.

Customise Record Constructor

All record classes have an implicit all argument constructor. However, we can customise the body of constructor.

public record StudentDto(
    Long id, String firstname, 
    String lastName, int year) {
  public StudentDto {
    if (year < 1999) {
      year = LocalDate.now().getYear();
    }
  }
}Code language: JavaScript (javascript)

In this example, we have added a constraint of the year field. If the year is older than 1999 we replace it with current year.

Next we will test this behaviour

@Test
public void testYearDefaultsToCurrentYear(){
  StudentDto studentDto = 
      new StudentDto(111L, "", "", 1998);

  assertEquals(LocalDate.now().getYear(), 
      studentDto.year());
}Code language: Java (java)

Record Class Features and Restrictions

Before we finish, it is important take a note of record class features and restrictions.

A record class,

  • is always final, hence cannot be extended.
  • cannot be marked abstract.
  • can have instance methods.
  • can override equals(), hashCode(), and toString() methods.
  • cannot extend another class.
  • can implement interfaces.

Summary

In this tutorial, we had an Introduction to Java Record Classes. Record classes are shallowly immutable classes, which avoid a lot of boiler plate code required to write an Immutable Data Object or a value object. Also, we have seen examples of Immutable Data Objects using Java Records and How to Customise Record Constructors.

Visit Introduction to Java for more java tutorials.


3 thoughts on “Create Immutable Value Objects using Java Record Classes

  1. Nice explanation sir.
    Please tell me whether immutable object and singleton object is same?
    Thank you.

    1. Thanks for commenting Yogesh.
      Singleton and Immutable are a different concepts.

      Singleton: A class restricts you to create one and only one instance of it.
      Immutable: A class allows you to set state of the instances but doesn’t allow changing the state. However, there is no restrictions how many instances it allows you to create.

      A Singleton a singleton can can be immutable but not required.Similarly, immutable can also be singleton but not required.

Comments are closed.