sormuras.github.io

Records with Dedicated Components

This is the third part of the miniseries about records. It revisits the copy idea introduced in the first part; this time without resorting to reflection.

Parts

  1. Records.copy
  2. Records.toTextBlock
  3. Records with Dedicated Components

Naming Conventions

Given a Person record and a demo program that asserts some basic properties.

public record Person(String name, String nickname) {

  public static void main(String[] args) {
    var person = new Person("Terence", "Nobody");

    assert "Terence".equals(person.name());
    assert "Nobody".equals(person.nickname());
    
    var mario = new Person("Mario", person.nickname());

    assert "Mario".equals(mario.name());
    assert "Nobody".equals(mario.nickname());
  }
}

As described in Functional transformation of immutable objects, Java might support the following syntax in the future:

var mario = person with { name = "Mario"; }

In the meanwhile, here’s a way to achieve something similar with minimal extra code. It leverages following features, all available since Java 17:

Here’s a line we could write today:

var mario = person.with(new Name("Mario"));

It uses a type with a deliberate chosen name close to its component name.

That’s the underlying code with an update demo program:

public record Person(String name, String nickname) {

  public sealed interface Component {}

  public record Name(String value) implements Component {}

  public record Nickname(String value) implements Component {}

  public Person(String name) {
    this(name, "");
  }

  public Person with(Component component) {
    return new Person(
        component instanceof Name name ? name.value : name,
        component instanceof Nickname nickname ? nickname.value : nickname);
  }

  public Person with(Component... components) {
    var copy = this;
    for (var component : components) copy = copy.with(component);
    return copy;
  }

  public static void main(String[] args) {
    var person =
        new Person("Terence")
            .with(new Nickname("Nobody"))
            .with(new Nickname("Somebody"), new Name("Mario"));

    assert "Mario".equals(person.name());
    assert "Somebody".equals(person.nickname());
    
    System.out.println(person); // Person[name=Mario, nickname=Somebody]    
  }
}

Open Ends