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.

Before You Start

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

What You'll Learn

The Big Idea — Ordering a Sandwich

💡 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.

1️⃣ The Problem — Telescoping Constructors

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.

2️⃣ The Solution — a Fluent Builder

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.

3️⃣ Validation in build()

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.

🧠 Quick Recall

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() .

Common Errors

Pro Tips

📋 Quick Reference

Concept

Syntax

Note

nested builder

static final class Builder

Inside the product

entry point

static Builder builder()

Factory method

required field

Builder(int size)

Constructor arg

optional default

int x = 30;

Field initializer

fluent setter

Builder x(v)

Returns this

chaining

.a().b().build()

Reads top to bottom

validation

throw in build()

Single check point

private ctor

private T(Builder b)

Build only via builder

build()

T build()

Returns immutable T

when to use

many optional params

Else use a record

in the wild

HttpRequest.newBuilder()

JDK example

📚 Resources / Keep Learning

❓ Frequently Asked Questions

🎉 Lesson Complete!

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.

Continue this course

Related lessons