0.If you’ve ever used Lombok’s @Builder
annotation on your entity or DTO classes, you might have run into an unexpected issue: default values on fields don’t get applied when you build an object. Instead, those fields end up being null
or their Java default (like 0
for int
).
I recently faced this problem in my project and want to share what I learned. Hopefully, this will save you some debugging time!
The Problem
Consider a simple entity class with a default value on one of its fields:
import lombok.Builder;
import lombok.ToString;
@Builder
@ToString
public class User {
private String name;
// Default value for status
private String status= "ACTIVE";
}
When you build a User
without specifying the status
, you might expect it to default to ACTIVE
:
User user = User.builder()
.name("Somename")
.build();
System.out.println(user);
Expected output:
User(name=Somename, status=ACTIVE)
Actual output:
User(name=Somename, status=null)
The status
field is set to null
— the Java default for String
is null
. This happens because Lombok’s @Builder
ignores the default value you assigned in the field declaration.
Why Does This Happen?
When Lombok generates the builder class, it creates private fields for each property without initializing them to the default values you assigned. Here is what Lombok generates:
public static class UserBuilder {
private String name;
private String status;
UserBuilder() {}
public UserBuilder name(String name) {
this.name = name;
return this;
}
public UserBuilder status(String status) {
this.status = status;
return this;
}
public User build() {
return new User(this.name, this.status);
}
public String toString() {
return "User.UserBuilder(name=" + this.name + ", status=" + this.status + ")";
}
}
The builder class stores values in its own private fields as you call builder methods. The build()
method calls the target class’s constructor with those stored values. If you never call .status(…)
on the builder, the corresponding builder field remains uninitialized (null
or Java default).
The Solution: Use @Builder.Default
To tell Lombok to respect the default value when using @Builder
, you need to annotate the field with @Builder.Default
:
import lombok.Builder;
import lombok.ToString;
@Builder
@ToString
public class User {
private String name;
@Builder.Default
private String status= "ACTIVE";
}
Now, it generates the Builder class like this:
public static class UserBuilder {
private String name;
private boolean status$set;
private String status$value;
UserBuilder() {
}
public UserBuilder name(String name) {
this.name = name;
return this;
}
public UserBuilder status(String status) {
this.status$value = status;
this.status$set = true;
return this;
}
public User build() {
String status$value = this.status$value;
if (!this.status$set) {
status$value = User.$default$status();
}
return new User(this.name, status$value);
}
public String toString() {
return "User.UserBuilder(name=" + this.name + ", status$value=" + this.status$value + ")";
}
}
Look at the status method closely. If we call .status(…)
, then it keeps a flag that we have set the status explicitly. Later in the build method, it checks whether we set the status or not. If not, then it assigns the default value that we gave at in time of initialization.
Now, when you build a user without specifying status
, Lombok will set it to ACTIVE
and you will get your expected output.
Key takeways
- Lombok’s
@Builder
does not use field initializers (default values) by default. - If you want a default value to be used when the builder property is not set explicitly, annotate the field with
@Builder.Default
. - Without
@Builder.Default
, fields default to Java’s default values (null
,0
,f
, etc.) when not set in the builder.
When to Use This
This issue usually pops up when:
- Your entity or DTO class uses Lombok
@Builder
. - You assign default values to fields using Java field initialization.
- You create objects with the builder but omit some fields expecting defaults to apply
Adding @Builder.Default
is a simple fix that preserves your default values in builder-created objects.
If you found this helpful, consider sharing it with your fellow Java developers! Lombok is great for reducing boilerplate, but these small gotchas can trip you up.
Happy coding