How do I use compact constructors in records?

Compact constructors in records are a concise way to initialize and validate fields of a record in Java. Unlike standard constructors, compact constructors omit the parameter list, as they operate directly on the record’s declared components.

Here’s how to use compact constructors in records:

1. Syntax:

A compact constructor is declared without parentheses after the constructor name. Inside the constructor body, you can initialize or validate fields of the record.

Example:

public record Person(String name, int age) {
    // Compact constructor
    public Person {
        if (name == null || name.isBlank()) {
            throw new IllegalArgumentException("Name cannot be null or blank");
        }
        if (age < 0) {
            throw new IllegalArgumentException("Age cannot be negative");
        }
    }
}

2. How It Works:

  • The compact constructor implicitly takes all the components of the record as parameters.
  • You can directly access these fields without explicitly specifying them as parameters since they are already “properties” of the record.
  • Unlike regular constructors, compact constructors aim to reduce boilerplate validations and/or additional initialization.

In the above example:

  • name and age are automatically initialized in the generated constructor, but the compact constructor validates them before allowing that to happen.
  • If the validations fail, the constructor throws exceptions.

3. Special Notes:

  • The Java compiler ensures that the compact constructor assigns values to all record components before the constructor completes execution.
  • You cannot directly modify the record components since they are implicitly final. What you can do is validate or make use of the incoming values.

4. Why Use Compact Constructors?

Compact constructors are helpful when:

  1. You need to validate fields during record initialization.
  2. You want to add extra logic (like logging) to the generated canonical constructor of the record.
  3. You want to reduce boilerplate by avoiding redundant parameter declarations.

5. Example with Additional Fields:

If the record has additional fields beyond what is declared in the record header, those can also be initialized in the compact constructor:

public record Rectangle(int length, int width) {
    private static final int MINIMUM_SIZE = 1;

    public Rectangle {
        // Validation
        if (length < MINIMUM_SIZE || width < MINIMUM_SIZE) {
            throw new IllegalArgumentException("Dimensions must be at least " + MINIMUM_SIZE);
        }
    }
}

Summary:

Compact constructors in records:

  • Automatically operate on the fields declared as record components.
  • Reduce boilerplate for constructor declarations.
  • Are ideal for validation and pre-processing logic.
  • Must initialize or ensure that all components are correctly set before completing.