Introducing the canonical and compact constructors for records – Record and record pattern

0 Comments 5:32 AM

84. Introducing the canonical and compact constructors for records

In the previous problem, we created the MelonRecord Java record and we instantiated it via the following code:

MelonRecord melonr = new MelonRecord(“Cantaloupe”, 2600);

How is this possible (since we didn’t write any parameterized constructor in MelonRecord)? The compiler just followed its internal protocol for Java records and created a default constructor based on the components that we provided in the record declaration (in this case, there are two components, type and weight). This constructor is known as the canonical constructor and it is always aligned with the given components. Every record has a canonical constructor that represents the only way to create instances of that record.But, we can redefine the canonical constructor. Here is an explicit canonical constructor similar to the default one – as you can see, the canonical constructor simply takes all the given components and sets the corresponding instance fields (also generated by the compiler as private final fields):

public MelonRecord(String type, float weight) {
  this.type = type;
  this.weight = weight;
}

Once the instance is created it cannot be changed (it is immutable). It will serve the only purpose of carrying this data around your program. This explicit canonical constructor has a shortcut known as the compact constructor – this is specific to Java records. Since the compiler knows the list of given components it can accomplish its job from this compact constructor which is equivalent to the previous one:

public MelonRecord {}

Pay attention to not confuse this compact constructor with the one without arguments. The following snippets are not equivalent:

public MelonRecord {}   // compact constructor
public MelonRecord() {} // constructor with no arguments

Of course, it doesn’t make sense to write an explicit canonical constructor just to mimic what the default one does. So, let’s examine several scenarios when we redefine the canonical constructor to make sense.

Handling validation

At this moment, when we create a MelonRecord, we can pass the type as null, or the melon’s weight as a negative number. This leads to corrupted records containing non-valid data. Validating the record components can be handled in an explicit canonical constructor as follows:

public record MelonRecord(String type, float weight) {
  // explicit canonical constructor for validations
  public MelonRecord(String type, int weight) {
    if (type == null) {
      throw new IllegalArgumentException(
        “The melon’s type cannot be null”);
    }
    if (weight < 1000 || weight > 10000) {
      throw new IllegalArgumentException(“The melon’s weight
         must be between 1000 and 10000 grams”);
    }
    this.type = type;
    this.weight = weight;
  }
}

Or, via the compact constructor as follows:

public record MelonRecord(String type, float weight) {
  // explicit compact constructor for validations
  public MelonRecord {
    if (type == null) {
      throw new IllegalArgumentException(
        “The melon’s type cannot be null”);
    }
    if (weight < 1000 || weight > 10000) {
      throw new IllegalArgumentException(“The melon’s weight
        must be between 1000 and 10000 grams”);
    }
  }
}

Validation handling is the most common use case for explicit canonical/compact constructors. Next, let’s see two more less-known use cases.

Leave a Reply

Your email address will not be published. Required fields are marked *

Benchmarking Vector API – Arrays, collections and data structuresBenchmarking Vector API – Arrays, collections and data structures

105. Benchmarking Vector API Benchmarking Vector API can be accomplished via JMH. Let’s consider three Java arrays (x, y, z) each of 50,000,000 integers, and the following computation: z[i] =

Adding more artifacts in a record Certification Exams of Java Java Exams Tackling guarded record patterns Tackling records in Spring Boot Understanding records serialization

Covering Vector API structure and terminology 2 – Arrays, collections and data structuresCovering Vector API structure and terminology 2 – Arrays, collections and data structures

The Vector lanes A Vector<E> is like a fixed-sized Java array made of lanes. The lane count is returned by the length() method and is called VLENGTH. The lane count

Adding more artifacts in a record Certification Exams of Java Java Exams Tackling records in Spring Boot Understanding records serialization