All topics
library
intermediate

Builder Pattern in Java

Construct complex objects step-by-step with a fluent API, and understand where Lombok's @Builder fits.

A builder collects values one named call at a time, then constructs the object once.

Builder = ordering a custom pizza. You specify toppings one by one (fluent setters). When done, you submit the order (build()). The pizza arrives fully assembled (immutable object). You can't change it after baking.

Key Concepts

1
    public class Order {
      private final String id; private final String customer;
      private final int quantity; private final boolean express;
2
      private Order(Builder b) {
        this.id = b.id; this.customer = b.customer;
        this.quantity = b.quantity; this.express = b.express;
      }
3
      public static Builder builder() { return new Builder(); }
4
      public static class Builder {
        private String id, customer;
        private int quantity = 1;
        private boolean express;
5
        public Builder id(String v)       { this.id = v; return this; }
        public Builder customer(String v) { this.customer = v; return this; }
        public Builder quantity(int v)    { this.quantity = v; return this; }
        public Builder express(boolean v) { this.express = v; return this; }
6
        public Order build() {
          if (id == null) throw new IllegalStateException("id is required");
          return new Order(this);
        }
      }
    }
7
    Order o = Order.builder().id("A-1").customer("Asha").express(true).build();
8
What it replaces. A constructor with several parameters of the same type:
9
    new Order("A-1", "Asha", 1, true);       // which boolean is which?
    new Order("Asha", "A-1", 1, true);       // compiles, and is wrong
10
Swapping two String parameters compiles. The builder names each one, so it cannot happen.
String
11
And it keeps the object immutable. Setters would be the other way to get named assignment, at the cost of an object anyone can change afterwards.
12
The rules that make it work.
    private constructor      so the builder is the only way in
    return this              from every setter, for chaining
    validate in build()      the one place that sees everything
    defaults on the fields   quantity = 1 above
13
Validating in build() matters. A required field cannot be checked when it is set, because you do not yet know whether a later call will supply it.
build()
14
Lombok generates all of this from @Builder, with @Builder.Default for defaults and toBuilder = true for making a modified copy.
@Builder@Builder.DefaulttoBuilder = true
15
Do not reach for it too early. Two or three parameters of different types need a constructor or a static factory. The builder earns its weight at four or more, or when several are optional.
16
What the interviewer is probing.1. "When is a builder worth the code?" Probing: judgement rather than habit. Stalls: "Always, it is cleaner." Moves up: four or more parameters, or several optional ones — two or three of different types need only a constructor or static factory.
17
2. "Why validate in build() rather than in each setter?" Probing: the ordering problem. Stalls: "Either works." Moves up: a setter cannot know whether a later call will supply a required field; build() is the one place that sees the whole object.
18
3. "What does the builder give you that setters do not?" Probing: immutability. Stalls: "Named parameters." Moves up: the constructed object can be immutable, where setters leave it mutable for the rest of its life.
19
4. "How does Lombok @Builder relate to this?" Probing: whether you know what it generates. Stalls: "It is different." Moves up: it generates exactly this pattern, with @Builder.Default for defaults and toBuilder for modified copies.