Lombok's builder with mandatory parameters

Viewed 2978

If I add @Builder to a class. The builder method is created.

Person.builder().name("john").surname("Smith").build();

I have a requirement where a particular field is mandatory. In this case, the name field is mandatory. Ideally, I would like to declare it like so.

Person.builder("john").surname("Smith").build();

When googling i found many alternatives like overriding the builder implementation as below:

@Builder
public class Person {

    private String name;
    private String surname;

    public static PersonBuilder builder(String name) {
        return new PersonBuilder().name(name);
    }
}

And then use it like below:

Person p = Person.builder("Name").surname("Surname").build();

The problem with above approach is that it still provides the name() and PersonBuilder() method like below, which i don't want:

Person p = Person.builder("Name").surname("Surname").name("").build();
Person p = new Person.PersonBuilder().build;

Another approach is to add @lombok.nonnull check at name which will force to provide value for name while creating object. but it is a runtime check. it will not force me to provide value for name while creating object.

Is there any additional technique which lombok provides to achieve below:

 Person p = Person.builder("Name").surname("Surname").build();

Note: The builder() and name() should not be exposed. The only way to create Person object should be either above or below:

 Person p = Person.builder("Name").build();
3 Answers

You can't really do it with lombok, see the explanation from the library authors. But is it that complicated to roll this builder on your own?

public static class PersonBuilder {

    private final String name;
    private String surname;

    PersonBuilder(String name) {
        this.name = name;
    }

    public PersonBuilder surname(String surname) {
        this.surname = surname;
        return this;
    }

    public Person build() {
        return new Person(name, surname);
    }
        
}

with the same method that you already have:

    public static PersonBuilder builder(String name) {
        return new PersonBuilder(name);
    }

Try to make the builder private.

Did you check this comment Required arguments with a Lombok @Builder

I am pretty sure you will find out once read the thread one more time.

P.S. If you have a class with only two field better use directly a constructor.

Best Practice:

import lombok.Builder;
import lombok.NonNull;

@Builder(builderMethodName = "privateBuilder")
public class Person {
    @NonNull
    private String name;
    private String surname;

    public static class PersonNameBuilder {
        public PersonBuilder name(String name) {
            return Person.privateBuilder().name(name);
        }
    }
    
    private static class PersonExtraBuilder extends PersonBuilder{
        @Deprecated
        @Override
        public PersonBuilder name(String name) {
            return this;
        }
    }

    public static PersonNameBuilder builder(String name) {
        return new PersonNameBuilder();
    }

    private static PersonExtraBuilder privateBuilder(){
        return new PersonExtraBuilder();
    }
}

Usage:

PersonNameBuilder nameBuilder = Person.builder();
PersonBuilder builder = nameBuilder.name("John");
Person p1 = builder.surname("Smith").build();

// Or
Person p2 = Person.builder().name("John").surname("Smith").build();

// The last `.name("")` will not work, and it will be marked as Deprecated by IDE.
Person p3 = Person.builder().name("John").surname("Smith").name("").build();
Related