简体   繁体   中英

Provide default values when using Lombok @Builder on method level?

Let's suppose there is the following exemplary class which is not under my control, ie I cannot change its behavior.

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
public class RegistrationRequest {

    private String email;
    private String emailRepeat;
    private String password;
    private String passwordRepeat;
}

I'd like to use Lombok @Builder to build test data for this class, so I put the @Builder annotation on the method level like this:

public class SomeTestUtils {

    public static final String DEFAULT_EMAIL = "foo@bar.com";
    public static final String DEFAULT_EMAIL_REPEAT = "foo@bar.com";
    public static final String DEFAULT_PASSWORD = "my-password";
    public static final String DEFAULT_PASSWORD_REPEAT = "my-password";

    @Builder(builderMethodName = "aRegistrationRequest", setterPrefix = "with")
    public static RegistrationRequest buildRegistrationRequest(String email, String emailRepeat, String password, String passwordRepeat) {
        final RegistrationRequest request = new RegistrationRequest();
        request.setEmail(email);
        request.setEmailRepeat(emailRepeat);
        request.setPasswort(password);     
        return request;
    }
}

This allows me to create an object like this:

SomeTestUtils.aRegistrationRequest()
  .withEmail("foo@bar.com")
  .withEmailRepeat("foo@bar.com")
  .withPassword("my-password")
  .withPasswordRepeat("my-password")
.build();

But now I'd like to provide default value for any field which is not explicitly set. So if I did this, then an object with all the defaults shall be created:

SomeTestUtils.aRegistrationRequest().build();

Lombok developer here:

You can't, not directly. The code needs to be syntactically valid java before lombok gets to do its thing, and there isn't a feasible way (at least, we've never managed to think of something acceptable) to write what you want in java syntax. These things all simply won't work:

/* 1 */ void foo(int param = 5) {}
/* 2 */ void foo(@DefaultValue(5) int param) {}
/* 3 */ void foo(@DefaultValue("5") int param) {}
/* 4 */ void foo(int param) {
  defaultValues: {
    param = 5;
  }
}
  1. This simply won't even parse. Javac fails with a syntax error and lombok isn't even invoked. We can't do this.
  2. Annotation parameters need a specific type - you can't define an annotation that takes a parameter whose type is 'whatever'. We could make @IntDefaultValue , @StringDefaultValue , etc, but [A] that just lets you cover primitives, strings, Class<?> values, enums, and arrays of those types - and nothing else, and [B] that means you can only supply compile time constants. This is far too restrictive.
  3. This would 'work' but shoving actual java code in string literals is so ugly, we're not going to add that to lombok.
  4. This is the closest to feasible, but there's no way you're going to remember you have to write it exactly like that, and linting tools will get rather confused about it. It also clashes with the annoying habit of many java programmers to add final to every parameter (if you're one of those, I suggest you cut that out. If you hate mutating params, add a linting or IDE rule to prevent it, and remove that clutter from your code, please). It's also easy to typo the defaultValues: param and you get zero auto-complete help.

Okay, so what do I do instead?

If null is not a sensible value for the stuff you want to provide defaults for, treat null as "I wanted the default". Something like:

@Builder void foo(Integer x) {
  if (x == null) x = 5;
  // carry on with the code as normal
}

or if you prefer never mutating your params (again I implore you - sanity checks and defaulting behaviour SHOULD be applied to the parameter; failure to do so means it is way too easy to accidentally use the 'wrong' one), you can do this:

@Builder void foo(Integer paramIn) {
  int param == paramIn == null ? 5 : paramIn;
  // carry on as normal. Woe is you if you use paramIn.
}

I suggest you add some javadoc that explains this, of course.

If null is a sensible option, but should not be the default, we're mostly in 'wow, that is so rare and exotic, it is no longer boilerplate' territory.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM