Sunday, February 14, 2021

Record classes in Java

The main idea behind Record classes introduced in Java 14 as a preview feature and enhanced with additional features in Java 15 is to eliminate ceremonies associated with creating plain data aggregate classes. 

Consider for example a Cat class which has one attribute - a String that represents the color of the Cat. At a bare minimum, we will have an instance variable to hold the color attribute, a constructor to initialize the Cat object and accessor methods for the single instance variable.

Code for this simple Cat implementation is given below

public class Cat {

    private String color;

    public Cat(String color) {
        this.color = color;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

}

Now lets strengthen this class implementation to production grade. We will add the implementation for these 3 methods to our Cat class

1. equals() - which returns true if the two cats compared has the same color

2. hashCode() - which should return the same value for two cat objects if they are equal as determined by the equals() implementation

3. toString() - which returns readable String representation for the Cat object. 

Below is the code with these 3 methods added to the Cat class

import java.util.Objects;

public class Cat {

    private String color;

    public Cat(String color) {
        this.color = color;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public boolean equals(Object that) {
        if (this == that) {
            return true;
        } else if (!(that instanceof Cat)) {
            return false;
        } else {
            Cat thatCat = (Cat) that;
            return Objects.equals(this.color, thatCat.color);
        }
    }

    @Override
    public int hashCode() {
        return this.color.hashCode();
    }

    @Override
    public String toString() {
        return String.format("Cat[color=%s]", this.color);
    }
}

Now we want to make this class immutable. We do this by removing the only setter method in the class definition - the setColor() method.

We also want to make it non-extendable so that no other class inherits from this class. This we achieve of course by making the class final. 

Final class definition with all these changes is:

import java.util.Objects;

public final class Cat {

    private String color;

    public Cat(String color) {
        this.color = color;
    }

    public String getColor() {
        return color;
    }

    @Override
    public boolean equals(Object that) {
        if (this == that) {
            return true;
        } else if (!(that instanceof Cat)) {
            return false;
        } else {
            Cat thatCat = (Cat) that;
            return Objects.equals(this.color, thatCat.color);
        }
    }

    @Override
    public int hashCode() {
        return this.color.hashCode();
    }

    @Override
    public String toString() {
        return String.format("Cat[color=%s]", this.color);
    }
}

The above code can be replaced by a simple record definition code below

public record Cat(String color) {}

So a record allows us to create plan data aggregation classes through simple definition eliminating the need to create ceremonial code needed with regular class definition. 

And yes... java converts record definition into equivalent class during compilation. We will explore in-depth, the characteristics and behavior of record classes in the next few posts.

Structure of record definition

Restrictions with record classes

More restrictions with using record classes

Compact constructor for Record classes

Serialization of Record with circular references

Reflection support for record classes

 

Sample code used in this post can be downloaded from https://github.com/ashokkumarta/awesomely-java/tree/main/2021/02/Language-Features/Record/Record-classes

No comments:

Post a Comment