The Builder Pattern

The Builder pattern constructs a complex, usually immutable object step by step through a fluent helper class — replacing confusing multi-argument constructors with readable, named, chainable calls.

Learn The Builder Pattern in our free Java course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.

Part of the free Java course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

You should know classes, constructors and access modifiers , and ideally the previous lesson on immutability — builders and immutable objects are natural partners.

💡 Analogy: A telescoping constructor is like ordering by shouting " one, twelve, true, false, true! " and hoping the kitchen guesses what each value means. A Builder is the sandwich shop counter: you say " start with a footlong, add cheese, add mushrooms, hold the pepperoni ", one clear instruction at a time, and only when you say " that's it " ( build() ) does the kitchen check your order is valid and hand over the finished, sealed sandwich. You configured a mutable order; you received an immutable meal.

Each instruction is named , so nobody confuses cheese for mushrooms — and the order can't go out the door until it passes the final check.

With several optional fields you end up with a ladder of constructors, and call sites become a row of mystery true / false values. Which flag is which? You can't tell without opening the class. Add one option and you add another constructor.

Move construction into a static nested Builder . Required values go to the builder's constructor; optional ones get named methods that set a field and return this so calls chain. The product's constructor is private and takes the builder, so objects can only be made the validated way.

The setters just record values; build() is the one place where the whole object exists, so it's where you validate. Check required fields and cross-field rules there, throwing IllegalStateException if anything is off — an invalid object is then never created.

Answer: chaining would break — builder.cheese().mushroom() wouldn't compile because cheese() would return nothing for mushroom() to call. Each fluent method must return this .

Answer: so the only path to an instance is through the builder's build() , which guarantees validation runs and the object is complete. A public constructor would let callers bypass all of that.

Answer: in build() . Only there does the whole object exist, so cross-field rules can be checked. Setters just record intent.

🎯 YOUR TURN — Finish the Burger builder

Complete the nested Builder : a patties(int) setter, a no-arg cheese() flag, and build() — all chainable.

🧩 MINI-CHALLENGE — Config builder with defaults & validation

Build an immutable HttpRequestConfig with a required URL, defaulted method/timeout, and validation in build() .

You can now recognise the telescoping-constructor problem, write a static nested Builder with fluent return this setters, validate in build() , produce an immutable result through a private constructor, and judge when a builder beats a plain constructor or record.

Next up: Dependency Injection — wiring objects together from the outside so your code is decoupled and testable.

Practice quiz

What problem does the Builder pattern solve?

  • Slow loops
  • Memory leaks
  • Telescoping constructors with many parameters
  • Thread starvation

Answer: Telescoping constructors with many parameters. It replaces a pile of confusing multi-argument constructors with readable, named, step-by-step construction.

Where is the Builder usually defined?

  • As a static nested class inside the product
  • In a separate package
  • As a global function
  • In the main method

Answer: As a static nested class inside the product. The Builder is conventionally a static nested class of the class it builds, with access to its private constructor.

Why do fluent setter methods return 'this'?

  • To save memory
  • To make them static
  • Required by the compiler
  • To allow method chaining

Answer: To allow method chaining. Returning the builder itself lets you chain calls: builder.cheese().mushroom().build().

What does the build() method typically do?

  • Nothing
  • Validate, then create the immutable product
  • Print the object
  • Reset the builder

Answer: Validate, then create the immutable product. build() is the single place to validate the accumulated state and then construct the final, usually immutable, object.

Why make the product's constructor private?

  • So objects can only be created through the Builder
  • For speed
  • To allow subclassing
  • It's optional styling

Answer: So objects can only be created through the Builder. A private constructor forces all construction through the builder, guaranteeing validation runs and the object is fully formed.

How are required vs optional fields usually handled?

  • All in setters
  • All optional
  • Required via the builder constructor, optional via fluent setters
  • Required via build()

Answer: Required via the builder constructor, optional via fluent setters. Required values are passed to the Builder's constructor; optional ones get fluent setters with sensible defaults.

The object produced by a good builder is typically...

  • mutable
  • immutable
  • static
  • abstract

Answer: immutable. Builders pair naturally with immutability — the builder mutates, the finished product does not.

When is the Builder pattern most worth it?

  • For a 1-field class
  • For utility methods
  • For enums
  • For classes with many optional parameters

Answer: For classes with many optional parameters. It shines when there are several optional parameters; for one or two fields a plain constructor is simpler.

What does method chaining look like?

  • b; cheese; build;
  • b.cheese().mushroom().build()
  • build(b, cheese, mushroom)
  • new B(cheese, mushroom)

Answer: b.cheese().mushroom().build(). Each fluent method returns the builder so the next call attaches directly, ending in build().

Which JDK class uses a builder-like fluent API?

  • ArrayList
  • Integer
  • HttpRequest.newBuilder()
  • Math

Answer: HttpRequest.newBuilder(). HttpRequest.newBuilder()...build() and Stream.Builder are real fluent-builder examples in the JDK.