Java Records

A record is a class whose whole purpose is to carry data. You declare its components in the header, and the compiler writes the constructor, the accessors, equals, hashCode and toString. Nothing a data carrier needs is left for you to type by hand.

Records arrived in JDK 16 after two preview rounds, and they are the shortest path from "I need a pair of values" to a type that behaves correctly inside a collection. Demo 33 in the Lab Examples shows one with a validating constructor.

JDK 14 introduces records, which are a new kind of type declaration. Like an enum, a record is a restricted form of a class. It’s ideal for "plain data carriers," classes that contain data not meant to be altered and only the most fundamental methods such as constructors and accessors.
// A record declares its components in the header.
record Rectangle(double length, double width) { }

// The constructor, accessors, equals, hashCode and toString come for free.
Rectangle r = new Rectangle(4, 5);
System.out.println(r);                              // Rectangle[length=4.0, width=5.0]
System.out.println(r.length());                     // 4.0
System.out.println(r.equals(new Rectangle(4, 5)));  // true

Generated Members

A record class declares the following members automatically:

  1. For each component in the header, the following two members:
  2. A private final field with the same name and declared type as the record component. This field is sometimes referred to as a component field.
  3. A public accessor method with the same name and type of the component; in the Rectangle record class example, these methods are Rectangle::length() and Rectangle::width().
  4. A constructor whose signature is the same as the header. This constructor assigns each argument from the new expression that instantiates the record class to the corresponding component field.
  5. Implementations of the equals and hashCode methods, which specify that two record classes are equal if they are of the same type and contain equal component values.
  6. An implementation of the toString method that includes the string representation of all the record class's components, with their names.

Customizing a Record

A record is not a straitjacket. You may add methods, static factories, and a compact constructor that validates the components before they are assigned. What you cannot add is a mutable field or a setter: a record is immutable by construction, and that is the property that makes it safe to share between threads.
record Rectangle(double length, double width) {

  // Compact constructor: no parameter list, the components are in scope.
  Rectangle {
    if (length <= 0 || width <= 0) {
      throw new IllegalArgumentException("invalid dimensions");
    }
  }

  // Records may still declare extra behaviour.
  double area() {
    return length * width;
  }
}

Full details, including the rules for generic and local records, are in the JDK Records manual.