javajava-record

Java records does not have default builder


Java came with records which are really useful and can avoid use of library like Project Lombok. But can someone please help me understand why records does not support Builder pattern by default? I understand records are immutable but Builder pattern also gives immutability.

So basically I have to use Lombok's @builder in records or implement by my own to use builder pattern, which could also be avoided if records allow builder by default.


Solution

  • Records are not “Lombok-lite”

    Recreating Lombok is not the purpose of Java record.

    The purpose of Java record is to provide an explicit vehicle for transparently communicating shallowly-immutable data.

    Technically, records are Java’s nominal tuple. Tuple means an ordered set of fields. Nominal means the fields have names, as does their container. Practically, you can think of a Java record as a struct on steroids.

    See the official document for scope and purpose, JEP 395: Records.

    So no Builder feature need be provided by default as that lies outside the scope of Java record. However, you can easily create your own Builder for any particular record class.

    AI-generated

    Actually, you need not write your own Builder implementation. Let a generative AI draft the code on your behalf. (Be sure to review carefully as these AIs are imperfect.)

    For example:

    write code for a Builder class for the Java record record Person ( String name , LocalDate birth ){}

    Ecosia search engine produces:

    public record Person(String name, LocalDate birth) {
        
        public static class Builder {
            private String name;
            private LocalDate birth;
    
            public Builder name(String name) {
                this.name = name;
                return this;
            }
    
            public Builder birth(LocalDate birth) {
                this.birth = birth;
                return this;
            }
    
            public Person build() {
                return new Person(name, birth);
            }
        }
    }
    

    That code worked for me, copy-pasted, with no edits needed.

    Usage:

    Person person = new Builder()
        .birth(LocalDate.of(2001, Month.JANUARY, 23))
        .name("Alice")
        .build();
    
    person.toString() // ➡️ Person[name=Alice, birth=2001-01-23]
    

    Withers

    By the way, work is underway to add “withers”, a feature to instantiate a new record object based on the payload values of another. See JEP 468: Derived Record Creation (Preview).

    RecordBuilder library

    RecordBuilder is an interesting project that lets you mark a Java record with an annotation. Then RecordBuilder generates code for builders and withers.

    I’ve not yet tried using this library, so I cannot vouch for it.