9

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();
rohit
  • 177
  • 4
  • 12

3 Answers3

3

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);
    }
Eugene
  • 117,005
  • 15
  • 201
  • 306
1

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.

Traycho Ivanov
  • 2,887
  • 14
  • 24
  • 1
    Thanks for the response. I checked the thread and i found that few things were unanswered. Making the builder private solved one of the issue. but my another concern is that the `name()` method is visible and one can use it like below: `Person.builder("Name").surname("Surname").name("").build();` in this case the value provided in the `builder()` will be changed. – rohit Jul 29 '20 at 12:57
  • 1
    @rohit I believe you have to write own builder and not use Lombok for your specific case as it seems, fields are still public. – Traycho Ivanov Jul 29 '20 at 14:46
0

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();
dallaslu
  • 444
  • 4
  • 7