java · level 3 · unit 7 · lesson 2
- meet it
- guess first
- your turn
- fix it
- nailed it
Tap a green word if you get lost.
meet it
A collects settings, then builds the object in one go.
A constructor call like new Coffee("latte", 2, "oat", true, false) is unreadable: which true means what? A builder fixes this. A static class Builder inside Coffee keeps the settings, with a named method for each, and anything you never set keeps a sensible default.
Each method sets one field and ends with return this, so calls chain into a sentence: new Coffee.Builder("latte").milk("oat").shots(2).build(). The Coffee constructor is private, so the builder is the only way in.
build() is the place to . It checks the settings and throws IllegalArgumentException with a clear message before a bad object can exist. The finished object can then be immutable, every field final.
public class Main {
static class Coffee {
private final String drink;
private final int shots;
private final String milk;
private Coffee(Builder b) { drink = b.drink; shots = b.shots; milk = b.milk; }
@Override public String toString() { return drink + " (shots: " + shots + ", milk: " + milk + ")"; }
static class Builder {
private final String drink;
private int shots = 1;
private String milk = "whole";
Builder(String drink) { this.drink = drink; }
Builder shots(int count) { shots = count; return this; }
Builder milk(String type) { milk = type; return this; }
Coffee build() { return new Coffee(this); }
}
}
public static void main(String[] args) {
System.out.println(new Coffee.Builder("latte").milk("oat").shots(2).build());
System.out.println(new Coffee.Builder("flat white").build());
}
} The nested Builder may read the private fields of Coffee, and the other way round, because they share one outer class.
guess first
Nine shots? What does build say?
Guess before you run it. Wrong guesses are how you learn, and nothing is marked.
public class Main {
static class Coffee {
private final String drink;
private final int shots;
private Coffee(Builder b) { drink = b.drink; shots = b.shots; }
@Override public String toString() { return drink + " (shots: " + shots + ")"; }
static class Builder {
private final String drink;
private int shots = 1;
Builder(String drink) { this.drink = drink; }
Builder shots(int count) { shots = count; return this; }
Coffee build() {
if (shots < 1 || shots > 4) {
throw new IllegalArgumentException("shots must be 1 to 4, got " + shots);
}
return new Coffee(this);
}
}
}
public static void main(String[] args) {
System.out.println(new Coffee.Builder("americano").shots(2).build());
System.out.println(new Coffee.Builder("rocket fuel").shots(9).build());
}
}
your turn
Your turn. Order your own drink.
Name a drink on line 19, then chain milk and shots on line 20 before build.
output
press run to see what happens
fix it
The builder will not chain. Can you fix it?
output
press run to see what happens
- Java says
missing return statementon line 14. What should a builder method hand back? - Each builder method returns the builder itself, so the next call can chain on.
- Change line 14 to
Builder milk(String type) { milk = type; return this; }, then run.
nailed it
you can build complex objects cleanly. :D
- A builder sets one option per method and keeps defaults for the rest.
return thislets builder calls chain into a fluent sentence.build()fails fast, so a bad object never exists.
new words in your collection
- builder
- fluent
- fail fast
This lesson isn’t saved. A free account remembers every lesson you finish.