When we define an extensible builder, one “problem” is that the methods of the base builder have to define a return type of the sub-type. This can be archived by an self-referencial generic (see http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ205 and http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ206)
class BaseBuilder<T extends BaseBuilder<T>> {
T baseElement() {
return (T) this;
}
}
class ExtendedBuilder extends BaseBuilder<ExtendedBuilder> {
ExtendedBuilder extendendElement() {
return this;
}
}
void testBaseBuilder() {
BaseBuilder<?> builder = new BaseBuilder<>();
builder.baseElement().build();
}
void testExtendedBuilder() {
ExtendedBuilder builder = new ExtendedBuilder();
builder.baseElement().extendendElement().build();
builder.extendendElement().baseElement().build();
}
The builder has some mandatory and some optional elements.
Builder
classbuild()
methodinterface Mandatory_2 {
Mandatory_3 mandatory_2();
}
interface Mandatory_3 {
OptionalElements mandatory_3();
}
interface OptionalElements {
String build();
OptionalElements optional_1();
OptionalElements optional_2();
OptionalElements optional_3();
}
class Builder implements Mandatory_2, Mandatory_3, OptionalElements {
public static Mandatory_2 mandatory_1() {
return new Builder();
}
public String build() {
return "MY RESULT";
}
public Mandatory_3 mandatory_2() { ... ; return this;}
public OptionalElements mandatory_3() { ... ; return this;}
public OptionalElements optional_1() { ... ; return this;}
public OptionalElements optional_2() { ... ; return this;}
public OptionalElements optional_3() { ... ; return this;}
// invocations
void test() {
Builder.mandatory_1().mandatory_2().mandatory_3().build();
Builder.mandatory_1().mandatory_2().mandatory_3().optional_1().build();
Builder.mandatory_1().mandatory_2().mandatory_3().optional_3().build();
Builder.mandatory_1().mandatory_2().mandatory_3().optional_1().optional_2().build();
Builder.mandatory_1().mandatory_2().mandatory_3().optional_2().optional_1().optional_3().build();
}