DEV Community

Cover image for Records: Data Carrier classes in Java 14
Vipin Sharma
Vipin Sharma

Posted on

Records: Data Carrier classes in Java 14

Records

This feature is going to increase productivity for professional java developers. It simplifies coding, makes java code more concise and readable. Records provide a way to model data in java, example for data could be one row from the database table.

Common implementation use cases

1. Multiple return values: Often we encounter cases when we want to return multiple values from a method, for this we will have to create a class having values that we need to return. Record provides an easy way rather than writing boilerplate code.

2. Data transfer objects (DTO): Developers working with databases often write DTO which typically used for storage only, we can again reduce boilerplate code using java records.

Java code before and after Record

To write even a simple data carrier class we have to write a lot of boilerplate code. Like constructors, accessors, equals(), hashCode(), toString(), etc.
Following is one example showing Point class without using record.

class Point
{
 public final int x;   
 public final int y;    
 public Point(int x, int y) { this.x = x; this.y = y; }    
 public int getX() {...}   
 public int getY() {...}   
 public String toString() {...}   
 public boolean equals(Object o) {...}   
 public int hashCode() {...)  
}
Enter fullscreen mode Exit fullscreen mode

Record equivalent for Point class is following one line, WOW !

record Point(int x, int y){}
Enter fullscreen mode Exit fullscreen mode

In the above example you can see, the record has a name and a state description. The state description declares the components of the record. and y are here components of the record. The record can have a body as well, we will see this later in examples.

This feature is in preview mode. To use the preview feature we need to pass --enable-preview parameter. Sample command for RecordsJDK14.java is:

javac --enable-preview -source 14 com/vip/jdk14/JavaRecords.java
java --enable-preview com.vip.jdk14.JavaRecords
Enter fullscreen mode Exit fullscreen mode

We can use javap command to see compiled class:

javac  --enable-preview -source 14 Point.java
javap -p Point.class

Compiled from "Point.java"
public final class com.vip.jdk14.Point extends java.lang.Record {
  private final int x;
  private final int y;
  public com.vip.jdk14.Point(int, int);
  public java.lang.String toString();
  public final int hashCode();
  public final boolean equals(java.lang.Object);
  public int x();
  public int y();
}
Enter fullscreen mode Exit fullscreen mode

In the above output of the javap command we can see following are properties of Records, Records have:

  1. A private final field for each component in record declaration(state description). (In above example private final int x, private final int y)
  2. A public read accessor method for each component of Record, with the same name and type as the parameter. (public int x(), public int y())
  3. A public constructor, having the same arguments as the components of the record. This constructor initializes each field from the corresponding argument. (public com.vip.jdk14.Point(int, int))
  4. Implementations of equals and hashCode that say two records are equal if they are of the same type and contain the same state.
  5. An implementation of toString that includes the string representation of all the record components, with their names.

Records behave like normal classes except restrictions, following are few properties of the Records:

  1. They can be declared top level or nested, they can be generic.
  2. They can implement interfaces.
  3. They are instantiated via the new keyword.
  4. The record's body may declare static methods, static fields, static initializers, constructors, instance methods, and nested types.
  5. The record and the individual components in a state description can be annotated.
  6. We can define a nested record, which is implicitly static. It avoids an immediately enclosing instance that would silently add state to the record.

Records have 3 types of constructors we can define explicitly:

  1. Canonical constructor: It contains all components of record.
  2. Compact constructor: It doesn't have any parameter, it is always called when defined.
  3. Custom constructor: All others except above 2.

Following is an example of all 3 types of constructors we talked about.

record NameValue(String name, int value) {
    //canonical constructor (It contains all components of record)
    public NameValue(String name, int value) {
        this.name = name;
        this.value = value;
    }  
    //compact constructor, no parameter, always called when defined.
    public NameValue {
        if(value == 1) throw new RuntimeException(""); //throwing Exception whenever value is 1, this is to test if it is called always.
    }
    // Custom constructor
    public NameValue(int value) {
        this(null, value); // Necessary to avoid compilation error
        if(value == 2) throw new RuntimeException(""); // Throwing Exception to test when is this called.
    }
}
Enter fullscreen mode Exit fullscreen mode

Restrictions on record

Following code throws compilation error in extends, because Records are implicitly final.

record Base(int a) { } 
record Child(int a, int b) extends Base { }
Enter fullscreen mode Exit fullscreen mode

Similarly, we have a few more restrictions to follow for Records.

Restrictions on records can be divided into 3 categories:

  1. Restrictions that ensure the state description alone defines the representation
    1. Records cannot extend any other class
    2. Records cannot declare instance fields other than the private final fields which correspond to components of the state description. Any other fields which are declared must be static.
  2. Restrictions that emphasize the API of a record is defined solely by its state description, and cannot be enhanced later by another class or record.
    1. Records are implicitly final.
    2. Records cannot be abstract.
  3. This restriction embodies an immutable by default policy.
    1. The components of a record are implicitly final.

Why Record why not just tuples?

A central aspect of Java's philosophy is that "names" matter. A Person with properties firstName and lastName is clearer and safer than a tuple of String and String.

At the end

To learn the best java language features and get the best Java Jobs, download my ebook 5 steps to Best Java Jobs for Free.

Follow me on twitter @vipinbit to get daily tips like this on Java Language Features.

Resources

  1. https://openjdk.java.net/jeps/359
  2. https://cr.openjdk.java.net/~briangoetz/amber/datum.html
  3. https://github.com/Vipin-Sharma/JDK14Examples, this repository has all code we discussed in this post.

Discussion (0)