Builder Pattern with Static Inner Class in Java
Fast recall
Quick revision
Core answer
Interview-ready guide
Interview answer
You implement Builder with a static inner class inside the target class.
First, make the outer class constructor private so it can only be called from inside the class. That constructor takes the Builder as a parameter and copies all values from the Builder's fields into its own final fields. Then you create a public static Builder class nested inside.
It mirrors the outer class fields — required fields go in the Builder's constructor as parameters, optional fields are plain instance variables with default values. Each optional field gets a setter method that assigns the value and returns this so you can chain calls.
Finally, you write a build method that validates everything and calls the private outer constructor, passing this as the argument.
For example, in a CustomerProfile class, the Builder constructor takes user ID and email as required parameters. Then you'd have setter methods like phoneNumber that returns this, shippingAddress that returns this, and so on. The build method checks that email contains an at sign, then returns new CustomerProfile with this.
One key mistake freshers make is forgetting the static keyword on the Builder class. Without static, you'd need a CustomerProfile instance to create a Builder, which is backwards. Another mistake is leaving the outer constructor public, which lets people skip the Builder entirely.
Always make it private so the Builder is the only way to construct the object.
Builder Pattern with Static Inner Class in Java
Direct Answer
Create a static nested Builder class that mirrors the outer class fields, with chainable setter methods returning 'this' and a build() method that calls the private outer class constructor, passing itself as the argument.
Code Example
public class CustomerProfile {
private final Long userId;
private final String email;
private final String phoneNumber;
private final String shippingAddress;
private final String billingAddress;
private final boolean emailNotifications;
private CustomerProfile(Builder builder) { // private constructor
this.userId = builder.userId;
this.email = builder.email;
this.phoneNumber = builder.phoneNumber;
this.shippingAddress = builder.shippingAddress;
this.billingAddress = builder.billingAddress;
this.emailNotifications = builder.emailNotifications;
}
public static class Builder {
private final Long userId; // required field
private final String email; // required field
private String phoneNumber;
private String shippingAddress;
private String billingAddress;
private boolean emailNotifications = true; // default value
public Builder(Long userId, String email) {
this.userId = userId;
this.email = email;
}
public Builder phoneNumber(String phone) {
this.phoneNumber = phone;
return this; // enables chaining
}
public Builder shippingAddress(String address) {
this.shippingAddress = address;
return this;
}
public Builder billingAddress(String address) {
this.billingAddress = address;
return this;
}
public Builder emailNotifications(boolean enabled) {
this.emailNotifications = enabled;
return this;
}
public CustomerProfile build() {
if (email == null || !email.contains("@")) {
throw new IllegalStateException("Valid email required");
}
return new CustomerProfile(this); // pass Builder to private constructor
}
}
public Long getUserId() { return userId; }
public String getEmail() { return email; }
}
Common Mistakes & Pitfalls
- Forgetting the static keyword on the Builder class: Without static, you'd need an instance of the outer class to create a Builder, which is circular — the whole point is the Builder creates the instance.
- Making the outer class constructor public: This lets callers bypass the Builder entirely, breaking validation and defeating the pattern's purpose; the constructor must be private so Builder is the only way in.
- Returning void from setter methods: Freshers often write standard setters that return void, breaking method chaining; every setter must return this.
- No validation in build(): Skipping validation means you can construct invalid objects that fail later; build() should verify required fields are set and throw IllegalStateException if not.
Check your understanding
Answer these without looking back at the article.
- How does Lombok's @Builder annotation generate this code?
- What is the difference between Builder pattern and a plain setter-based approach?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.