Feature: mapper / transform setters (mapX(UnaryOperator<T>))
Summary
Add an optional, per-field mapper method that transforms the value currently set in the builder by applying a function to it, instead of overwriting it. This complements the existing setters (x(value), x(Supplier), x(Consumer)), and pairs naturally with the With copy-and-modify flow.
Motivation
Adjusting a value relative to its current state (trim, clamp, increment, uppercase, default-if-null) currently requires reading the value out, transforming it, and setting it again. A mapX method makes this a single fluent step — a capability FreeBuilder offers via mapProperty(UnaryOperator).
PersonBuilder.create()
.name(" bob ")
.mapName(String::trim) // "bob"
.mapName(String::toUpperCase) // "BOB"
.build();
orderBuilder.quantity(10).mapQuantity(q -> q * 2); // 20
Proposed generated shape
For each field x of type T, generate:
public XBuilder mapName(UnaryOperator<String> op) {
if (!this.name.isSet()) {
throw new IllegalStateException("Cannot map 'name' before it is set");
}
this.name = changedValue(op.apply(this.name.value()));
return this;
}
Behavior when the field is unset — decided
Calling mapX before the field has been set throws IllegalStateException with a clear message ("Cannot map 'name' before it is set").
Rationale:
- Fail-fast at the exact call site rather than producing a surprising
null passed into the user's function (which would NPE later and be hard to trace).
- Consistent with SB's existing style —
build() already throws IllegalStateException for unset required fields.
- Matches FreeBuilder's behavior for required fields.
A lenient variant is explicitly out of scope for the first cut; if wanted later, add it as a separate, clearly-named method (e.g. mapNameIfSet(...) that no-ops when unset) rather than overloading mapName with surprising behavior.
Scope / design notes
- Opt-in. Introduce a new option in
CompilerArgumentsEnum (e.g. generateMapperHelpers), off by default, consistent with the other helper-generation flags (generateVarArgsHelpers, generateStringFormatHelpers, generateAddToCollectionHelpers, ...). Also expose it via SimpleBuilder.Options and document under Helper Methods in docs/CONFIGURATION.md.
- Implement as a method generator component so it participates in the existing generator registry / component-filtering (
deactivateGenerationComponents).
- Uses the existing
TrackedValue model (isSet(), value(), changedValue(...)).
- Applies to all field kinds SB already generates setters for (constructor fields and setter fields).
Acceptance criteria
References
Feature: mapper / transform setters (
mapX(UnaryOperator<T>))Summary
Add an optional, per-field mapper method that transforms the value currently set in the builder by applying a function to it, instead of overwriting it. This complements the existing setters (
x(value),x(Supplier),x(Consumer)), and pairs naturally with theWithcopy-and-modify flow.Motivation
Adjusting a value relative to its current state (trim, clamp, increment, uppercase, default-if-null) currently requires reading the value out, transforming it, and setting it again. A
mapXmethod makes this a single fluent step — a capability FreeBuilder offers viamapProperty(UnaryOperator).Proposed generated shape
For each field
xof typeT, generate:Behavior when the field is unset — decided
Calling
mapXbefore the field has been set throwsIllegalStateExceptionwith a clear message ("Cannot map 'name' before it is set").Rationale:
nullpassed into the user's function (which would NPE later and be hard to trace).build()already throwsIllegalStateExceptionfor unset required fields.A lenient variant is explicitly out of scope for the first cut; if wanted later, add it as a separate, clearly-named method (e.g.
mapNameIfSet(...)that no-ops when unset) rather than overloadingmapNamewith surprising behavior.Scope / design notes
CompilerArgumentsEnum(e.g.generateMapperHelpers), off by default, consistent with the other helper-generation flags (generateVarArgsHelpers,generateStringFormatHelpers,generateAddToCollectionHelpers, ...). Also expose it viaSimpleBuilder.Optionsand document under Helper Methods indocs/CONFIGURATION.md.deactivateGenerationComponents).TrackedValuemodel (isSet(),value(),changedValue(...)).Acceptance criteria
mapX(UnaryOperator<T>)for each eligible field.mapXthrowsIllegalStateExceptionwith a clear message when the field is unset.docs/CONFIGURATION.md.Withcopy-and-modify.References