Status: future idea. This is a large, opt-in feature with a substantial increase in generated code (a chain of step interfaces per required field). It is captured for future consideration, not scheduled work.
Feature (future idea): opt-in staged / step ("telescoping") builder
Summary
Add an opt-in generation mode that produces a staged builder: instead of one builder type exposing all setters, the processor generates a chain of small step interfaces so the compiler forces required fields to be set, in order, before build() is reachable. Required-field violations become compile errors instead of the runtime IllegalStateException SB already throws.
Example
Person p = PersonBuilder.builder()
.name("Jane") // NameStep -> returns AgeStep
.age(30) // AgeStep -> returns BuildStep
.build(); // BuildStep -> build() only appears here
// PersonBuilder.builder().build(); // ❌ won't compile — name/age not set
// PersonBuilder.builder().age(30).name(..); // ❌ won't compile — wrong order
Generated shape (illustrative):
interface NameStep { AgeStep name(String n); }
interface AgeStep { BuildStep age(int a); }
interface BuildStep { Person build(); /* optional-field setters live here */ }
Relationship to existing behavior
SB already enforces required/non-null fields at runtime in build() (a field is required when it is a primitive, a constructor parameter, or carries a @NotNull/@NonNull-named annotation). Staged builders would be the compile-time complement to that runtime enforcement — the same "required" set, but caught by the type system instead of at build time.
Why this is a future idea, not scheduled work
- Massive codegen. Every required field adds a dedicated step interface plus the wiring between steps; the generated surface grows a lot compared to the current single-builder output. This is a significant increase in complexity and maintenance in the generator.
- Polarizing ergonomics. Forced ordering and many step types are loved by some teams and disliked by others; it makes the API more rigid.
- Marginal benefit over what exists. Runtime enforcement already prevents building an invalid object. Staged builders only move the failure from build time to compile time.
- "Required but nullable" barely exists. The niche this would most help — a field you must set but may set to
null — is essentially never needed in practice, so the extra machinery buys little.
If ever implemented — design constraints
- Strictly opt-in via a new option (e.g.
staged) in CompilerArgumentsEnum / SimpleBuilder.Options, off by default; the current single-builder output must remain the default and unchanged.
- Reuse the existing required-field detection (primitive / constructor param /
@NotNull/@NonNull).
- Optional (non-required) fields exposed on the final
BuildStep.
- Must cooperate with existing features where feasible (
With copy-and-modify, Jackson, access-level control, naming suffixes) or explicitly document interactions/limitations.
- Consider whether it fits the generator-registry / component model or needs a separate generation path.
References
Feature (future idea): opt-in staged / step ("telescoping") builder
Summary
Add an opt-in generation mode that produces a staged builder: instead of one builder type exposing all setters, the processor generates a chain of small step interfaces so the compiler forces required fields to be set, in order, before
build()is reachable. Required-field violations become compile errors instead of the runtimeIllegalStateExceptionSB already throws.Example
Generated shape (illustrative):
Relationship to existing behavior
SB already enforces required/non-null fields at runtime in
build()(a field is required when it is a primitive, a constructor parameter, or carries a@NotNull/@NonNull-named annotation). Staged builders would be the compile-time complement to that runtime enforcement — the same "required" set, but caught by the type system instead of at build time.Why this is a future idea, not scheduled work
null— is essentially never needed in practice, so the extra machinery buys little.If ever implemented — design constraints
staged) inCompilerArgumentsEnum/SimpleBuilder.Options, off by default; the current single-builder output must remain the default and unchanged.@NotNull/@NonNull).BuildStep.Withcopy-and-modify, Jackson, access-level control, naming suffixes) or explicitly document interactions/limitations.References