I have a class with a builder pattern that extends a class also with a builder (using lombok @SuperBuilder
).
It works fine, but the problem is that return type of methods in the parent builder are naturally of the parent builder - which means that chains breaks in the actual usage of the builders if I use any parent builder methods before any child builder methods.
@Builder
public class Activity {
protected String foo;
protected String bar;
public static abstract class ActivityBuilder {
public ActivityBuilder baz(String baz) {
// ... do something
return this;
}
}
}
@SuperBuilder
public class DetailedActivity extends Activity {
public static abstract class DetailedActivityBuilder extends ActivityBuilder {
public DetailedActivityBuilder platform(Platform platform) {
this.foo(platform.foo);
this.bar(platform.bar)
return this;
}
}
}
// Works because "DetailedActivityBuilder" methods come first:
DetailedActivityBuilder.builder().platform(platformData).foo()
// Fails because the parent builder returns ActivityBuilder
DetailedActivityBuilder.builder().foo().platform(platformData)"
Is there any approach to returning the correct type and allowing chaining to work in any order?
I thought about passing a generic to ActivityBuilder
but that only fixes custom methods - lombok generated methods still return the ActivityBuilder
type.
It is important that Activity
is @SuperBuilder
too. From documentation:
The
@SuperBuilder
annotation produces complex builder APIs for your classes. In contrast to@Builder
,@SuperBuilder
also works with fields from superclasses. However, it only works for types. Most importantly, it requires that all superclasses also have the@SuperBuilder
annotation.
Then, the builder classes need to match what lombok would have generated. It should have 2 type parameters, C
representing the type that is being built, and B
representing the builder type that the builder methods return.
Your custom methods should return B
, and use return self();
at the end, instead of return this;
. self()
is a method generated by @SuperBuilder
.
@SuperBuilder
class Activity {
protected String foo;
protected String bar;
public static abstract class ActivityBuilder<C extends Activity, B extends ActivityBuilder<C, B>> {
public B baz(String baz) {
// ... do something
return self();
}
}
}
@SuperBuilder
class DetailedActivity extends Activity {
public static abstract class DetailedActivityBuilder<C extends DetailedActivity, B extends DetailedActivityBuilder<C, B>> extends ActivityBuilder<C, B> {
public B platform(Platform platform) {
// do something else...
return self();
}
}
}