Builder Pattern in Java: Constructing Complex Objects Step by Step
The Problem
When an object has many optional fields, telescoping constructors (new Foo(a, b, null, null, c, ...)) become unreadable and error-prone. The Builder pattern separates the construction of a complex object from its representation, letting you build it step by step.
Diagram
%%{init: {'theme':'base', 'themeVariables': { 'primaryColor': '#eef2ff', 'primaryTextColor': '#1e293b', 'primaryBorderColor': '#818cf8', 'lineColor': '#94a3b8', 'secondaryColor': '#f0fdf4', 'secondaryBorderColor': '#86efac', 'tertiaryColor': '#fff7ed', 'tertiaryBorderColor': '#fdba74', 'background': '#ffffff', 'mainBkg': '#eef2ff', 'secondBkg': '#f0fdf4', 'tertiaryBkg': '#fff7ed', 'noteBkgColor': '#fff7ed', 'noteBorderColor': '#fdba74', 'actorBkg': '#eef2ff', 'actorBorder': '#818cf8' }}}%%
classDiagram
class CustomerOrder {
-String customerId
-List~Item~ items
-String discountCode
-Address shippingAddress
}
class CustomerOrderBuilder {
+customerId(String) CustomerOrderBuilder
+addItem(Item) CustomerOrderBuilder
+discountCode(String) CustomerOrderBuilder
+shippingAddress(Address) CustomerOrderBuilder
+build() CustomerOrder
}
CustomerOrderBuilder ..> CustomerOrder : buildsJava Example
public final class CustomerOrder {
private final String customerId;
private final List<Item> items;
private final String discountCode;
private final Address shippingAddress;
private CustomerOrder(Builder builder) {
this.customerId = builder.customerId;
this.items = List.copyOf(builder.items);
this.discountCode = builder.discountCode;
this.shippingAddress = builder.shippingAddress;
}
public static Builder builder() {
return new Builder();
}
public static final class Builder {
private String customerId;
private final List<Item> items = new ArrayList<>();
private String discountCode;
private Address shippingAddress;
public Builder customerId(String customerId) {
this.customerId = customerId;
return this;
}
public Builder addItem(Item item) {
this.items.add(item);
return this;
}
public Builder discountCode(String discountCode) {
this.discountCode = discountCode;
return this;
}
public Builder shippingAddress(Address address) {
this.shippingAddress = address;
return this;
}
public CustomerOrder build() {
Objects.requireNonNull(customerId, "customerId is required");
return new CustomerOrder(this);
}
}
}Usage:
CustomerOrder order = CustomerOrder.builder()
.customerId("CUST-001")
.addItem(new Item("SKU-1", 2))
.discountCode("WELCOME10")
.shippingAddress(new Address("CDMX", "MX"))
.build();
Quarkus Example
Builders pair naturally with Quarkus/RESTEasy Reactive DTOs and with building TMF API payloads, where TMF resources (like TMF622 Product Orders) have many optional fields:
@Path("/productOrder")
public class ProductOrderResource {
@POST
public ProductOrder create(ProductOrderRequest request) {
ProductOrder order = ProductOrder.builder()
.externalId(request.externalId())
.priority(request.priority() != null ? request.priority() : "4")
.relatedParty(mapParty(request))
.build();
return orderService.persist(order);
}
}
Quarkus doesn’t add a Builder-specific feature — the pattern stays plain Java — but it’s frequently used to assemble the record/DTO objects that flow through REST endpoints and MapStruct mappers.
Key Takeaways
- Use Builder when an object has many optional parameters or must be built in multiple steps.
- Making the target class immutable (final fields, no public constructor) plus a builder gives you thread-safety and validation at
build()time. - Very common in DTO/entity construction for TMF-style APIs where many fields are optional per use case.

