Each step returns a different interface that only exposes the next method to call, so the compiler itself rejects a build() before size() and topping() were both supplied. That removes the runtime validation an ordinary builder would otherwise need.
interface NeedSize { NeedTopping size(String size); }
interface NeedTopping { NeedBuild topping(String topping); }
interface NeedBuild { Sandwich build(); }
class Sandwich {
final String size, topping;
private Sandwich(String size, String topping) { this.size = size; this.topping = topping; }
public String toString() { return size + " sandwich with " + topping; }
static NeedSize builder() {
return size -> topping -> () -> new Sandwich(size, topping);
}
}
Sandwich s = Sandwich.builder().size("footlong").topping("turkey").build();
System.out.println(s);
footlong sandwich with turkey
Run this yourself in the Online Java Compiler, spin up a live REST API in the API Sandbox, or practise with Java interview questions.
Published 2026-09-27