@Builder
@Builder implements the builder pattern for you, giving callers a fluent, readable way to construct objects with many fields. It is especially valuable for DTOs and value objects that have several optional fields, where a long positional constructor would be hard to read and easy to get wrong.
Fluent construction
Annotate a class (or a constructor/static method) with @Builder and Lombok generates a static builder() method plus a chainable setter for each field.
import lombok.Builder;
import lombok.Getter;
@Getter
@Builder
public class UserDto {
private Long id;
private String name;
private String email;
private boolean active;
}
Callers construct instances fluently, naming each value explicitly:
UserDto user = UserDto.builder()
.id(1L)
.name("Ada Lovelace")
.email("[email protected]")
.active(true)
.build();
Compare this with a positional constructor call like new UserDto(1L, "Ada Lovelace", "[email protected]", true) — the builder is self-documenting and order-independent.
@Builder.Default for default values
A field initializer is ignored by the builder unless you mark it @Builder.Default. Without the annotation, an unset field falls back to the Java default (null, 0, false), not your initializer.
@Getter
@Builder
public class SearchRequest {
private String query;
@Builder.Default
private int page = 0;
@Builder.Default
private int size = 20;
}
SearchRequest req = SearchRequest.builder().query("lombok").build();
// req.getPage() == 0, req.getSize() == 20 (defaults applied)
Warning: Forgetting
@Builder.Defaultis a common bug. A field written asprivate int size = 20;will be0when built unless the annotation is present.
@Singular for collections
@Singular lets callers add collection elements one at a time and produces an immutable collection in the built object. Lombok also derives a sensible singular method name.
import lombok.Builder;
import lombok.Getter;
import lombok.Singular;
@Getter
@Builder
public class Order {
private Long id;
@Singular
private List<String> items; // add via .item(...)
@Singular("tag")
private Set<String> tags; // custom singular name
}
Order order = Order.builder()
.id(100L)
.item("Keyboard")
.item("Mouse")
.tag("electronics")
.tag("priority")
.build();
// order.getItems() == [Keyboard, Mouse] (immutable)
The builder also exposes plural methods (.items(collection)) and a .clearItems() method.
toBuilder for copy-and-modify
Set toBuilder = true to add an instance method that pre-fills a new builder with the current object’s values — ideal for creating a modified copy of an immutable object.
@Getter
@Builder(toBuilder = true)
public class Config {
private String host;
private int port;
private boolean tls;
}
Config base = Config.builder().host("localhost").port(8080).tls(false).build();
Config secure = base.toBuilder().tls(true).build(); // copies host/port, flips tls
Builders for DTOs in Spring Boot
@Builder pairs naturally with immutable response DTOs. Combine it with @Getter (not @Setter) so the object is read-only after construction, and let a service map an entity into the DTO:
@Service
@RequiredArgsConstructor
public class UserMapper {
public UserDto toDto(User entity) {
return UserDto.builder()
.id(entity.getId())
.name(entity.getName())
.email(entity.getEmail())
.active(entity.isActive())
.build();
}
}
This keeps construction explicit and the DTO immutable, which is the recommended shape for API responses.
Tip: Place
@Builderon a constructor or static factory method (rather than the class) when you want the builder to cover only a subset of fields, or to coexist with an@AllArgsConstructor. For deserialization of incoming JSON into a builder-based type, add Jackson’s@JsonDeserialize(builder = ...)or annotate with@Jacksonized.