Java Records Are Here - Available in JDK 14

2020 03 01 head

We need justification not to use immutable objects in our solutions, rather than an affirmative reason to use them. Records provide a first-class means for modeling data-only aggregates. They have been available in Java since the release of JDK 14.

Records are often seen as syntactic sugar to easily create immutable objects without having to write too much boilerplate code. Records as a clean definition of immutable objects can have a tremendous impact on your component design.

You as a designer have to reflect why your abstractions are not immutable and what is the rationale behind. From my experience, it improves the quality of your design.

Records guarantee:

  • An immutable class concept which is the strongest argument for the use of record.

  • Cannot change their state after construction.

  • Do not change the state of the whole system in any way.

  • Do not do side effects, like inputs and outputs.

Records provide

  • A set of boilerplate methods generated by the Java ecosystem. These methods are always semantically correct and efficient.

  • A public constructor initializing all instance variables Getters for all instance variables. The name of the getters is PropertyType property(). We get rid of the cumbersome and outdated convention PropertyType getProperty() propagated by Java Beans.

  • A semantic correct implementation of hashCode() and equals(). Your objects can be used in standard API collections without difficult to find bugs.

  • A human-readable implementation toString().

The huge advantage of the approach against IDE code generation is the guaranty that these methods are semantically always correct.

Advantages

The key benefits of immutable objects are:

  • Thread safety

  • Atomicity of failure

  • Absence of hidden side effects

  • Protection against null reference errors

  • Ease of caching

  • Prevention of identity mutation, see contract for API standard collections

  • Avoidance of temporal coupling between methods

  • Support for referential transparency

  • Protection from instantiating logically invalid objects

  • Protection from inadvertent corruption of existing objects.

Design Impact

I updated my open source projects to use the record abstraction. What a surprise! I found regularly undocumented trade-offs why a class was not implemented as an immutable type. Quite often, I was just lazy and took a shortcut.

Records motivate you as a clean coder and designer to decide why a class should be mutable or not. This pressure had a tremendous impact on the quality of my subsystem design. The Java language guarantees that all record classes must be immutable.

Immutable value objects are a cornerstone in the Domain Driven Design DDD approach.

When you are using Data Transfer Objects, DTO, they should probably always be records. In the long run, you should eliminate your DTO, they are just waste and break all the rules of abstraction and information hiding.

Tricks and Tips

You can define a constructor extension to validate the passed arguments. This validation block is automatically inserted at the beginning of the generated constructor.

 public record Range(int lo, int hi) {
    public Range {
        if (lo > hi) {
            // referring here to the implicit constructor parameters
            throw new IllegalArgumentException(String.format("(%d,%d)", lo, hi));
        }
    }
 }

if the record has more than a few instance variables, calling the constructor with various sets of default values can become cumbersome.

Two approaches easily mitigate this problem.

  • Create static factory methods taking care of default parameter values for simple situations. Look at the standard API List class and the set of static methods of.

  • Implement the builder pattern to provide full flexibility to the consumers of your class.

Start using the new concept of record in your source code and implicitly improve the quality of your design. At the same time, write less boilerplate code and enjoy coding.