Skip to content

Record Builder

pawel_labaj edited this page Aug 2, 2023 · 11 revisions

Builders are a useful pattern that allow you to specify a set of optional parameters when creating a record, which can improve readability and reduce the need for constructor overloading.

AutoRecord provides support for generating builders using the Randgalt/record-builder library.

Enabling Builder Generation

To enable builder generation for a given interface, simply add the @AutoRecord.Options(withBuilder = true) annotation to it. This will cause AutoRecord to add the @RecordBuilder annotation to generated record and this will cause generating a builder in a next round of annotation processing.

The generated record includes a static builder() method, which return a new builder instance. It includes also a toBuilder() method, which returns a builder instance with the values from the existing record.

Example interface and generated record

Here's an example interface:

@AutoRecord
@AutoRecord.Options(withBuilder = true)
interface Person {
    String name();
    int age();
}

Here's the corresponding generated record that demonstrates builder generation:

@Generated("pl.com.labaj.autorecord.AutoRecord")
@GeneratedWithAutoRecord
@RecordBuilder
@RecordBuilder.Options(
        addClassRetainedGenerated = true
)
record PersonRecord(String name, int age) implements Person {
    PersonRecord {
        requireNonNull(name, "name must not be null");
    }

    static PersonRecordBuilder builder() {
        return PersonRecordBuilder.builder();
    }

    PersonRecordBuilder toBuilder() {
        return PersonRecordBuilder.builder(this);
    }
}

You can create the record instance with using builder:

PersonRecord person = PersonRecord.builder()
    .name("Joe")
    .age(25)
    .build();

PersonRecord olderPerson = person.toBuilder()
    .age(50)
    .build();

It is recommended to provide in your interface:

  • a factory method with a clear and descriptive name to get the record builder
  • an abstract toBuilder() method that is implemented in the record

Here is an example of what it might look like:

@AutoRecord
@AutoRecord.Options(withBuilder = true)
interface Person {
    String name();
    int age();

    static PersonRecordBuilder builder() {
        return PersonRecord.builder();
    }

    PersonRecordBuilder toBuilder();
}

Customizing Builder Generation

If you want to customize the builder generation process, you can add the RecordBuilder.Options annotation to the interface.

Example interface and generated record

Here's an example interface:

@AutoRecord
@AutoRecord.Options(withBuilder = true)
@RecordBuilder.Options(suffix = "_Creator")
interface PersonB {
    String name();
    int age();
}

Here's the corresponding generated record that demonstrates builder generation:

@Generated("pl.com.labaj.autorecord.AutoRecord")
@GeneratedWithAutoRecord
@RecordBuilder
@RecordBuilder.Options(
        addClassRetainedGenerated = true,
        suffix = "_Creator"
)
record PersonBRecord(String name, int age) implements PersonB {
    PersonBRecord {
        requireNonNull(name, "name must not be null");
    }

    static PersonBRecord_Creator builder() {
        return PersonBRecord_Creator.builder();
    }

    PersonBRecord_Creator toBuilder() {
        return PersonBRecord_Creator.builder(this);
    }
}
📝 Note
It is recommended to use builder() and toBuilder() methods from an interface or generated record and not from generated builder.

For more information, refer to the RecordBuilder library's documentation.