Thursday, 4 May 2023

Record Classes in Java 14

Record classes, which are a special kind of class in Java-14, help to model plain data aggregates with less ceremony than normal classes. 

For background information about record classes, see JEP 395.

 Why do we need the Record Classes?

One of the common complaints with Java has been its verbosity. If you have to create a simple POJO class, it requires the following boilerplate code.

  • Private fields
  • Getter and Setter Methods
  • Constructors
  • hashCode(), equals(), and toString() methods.

This verbosity is one of the reasons for the high interest in Kotlin and Lombok.

For example, here is a record class with two fields:

record Rectangle(double length, double width) { }

This concise declaration of a rectangle is equivalent to the following normal class:

public final class Rectangle {

private final double length;
private final double width;

public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}

public double getLength() {
return length;
}

public double getWidth() {
return width;
}

// An implementation of toString() that returns a string
// representation of all the record class's fields,
// including their names.
@Override
public String toString() {
return "Rectangle{" +
"length=" + length +
", width=" + width +
'}';
}

// Implementation of equals() and hashCode(), which specify
// that two record objects are equal if they
// are of the same type and contain equal field values.
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Rectangle rectangle = (Rectangle) o;
return Double.compare(rectangle.length, length) == 0 && Double.compare(rectangle.width, width) == 0;
}

@Override
public int hashCode() {
return Objects.hash(length, width);
}
}

As record classes are just special kinds of classes, you create a record object (an instance of a record class) with the new keyword, for example:

Rectangle r = new Rectangle(4,5);

The Canonical Constructor of a Record Class

The following example explicitly declares the canonical constructor for the Rectangle record class. It verifies that length and width are greater than zero. If not, it throws an IllegalArgumentException:
record Rectangle(double length, double width) {
public Rectangle(double length, double width) {
if (length <= 0 || width <= 0) {
throw new java.lang.IllegalArgumentException(
String.format("Invalid dimensions: %f, %f", length, width));
}
this.length = length;
this.width = width;
}
}

Repeating the record class's components in the signature of the canonical constructor can be tiresome and error-prone. To avoid this, you can declare a compact constructor whose signature is implicit (derived from the components automatically).

For example, the following compact constructor declaration validates length and width in the same way as in the previous example:

record Rectangle(double length, double width) {
public Rectangle {
if (length <= 0 || width <= 0) {
throw new java.lang.IllegalArgumentException(
String.format("Invalid dimensions: %f, %f", length, width));
}
}
}

This succinct form of constructor declaration is only available in a record class. Note that the statements this.length = length; and this.width = width; which appear in the canonical constructor but do not appear in the compact constructor. At the end of a compact constructor, its implicit formal parameters are assigned to the record class's private fields corresponding to its components.

Important Points about Record Classes

  1. A Record class is final, so we can’t extend it.
  2. The Record classes implicitly extend java.lang.Record class.
  3. All the fields specified in the record declaration are final.
  4. The record fields are “shallow” and immutable and depend on the type. For example, we can change the addresses field by accessing it and then making updates to it.
  5. A single constructor is created with all the fields specified in the record definition.
  6. The Record class automatically provides accessor methods for the fields. The method name is the same as the field name, not like generic and conventional getter methods.
  7. The Record class provides hashCode(), equals(), and toString() implementations too.

Conclusion

Java Records is a welcome addition to the core programming features. You can think of it as a “named tuple”. It’s meant to create a data carrier object with a compact structure, avoiding all the boiler-plate code.


No comments:

Post a Comment