Consider the abstract Data class with an abstract Builder:
abstract class Data {
abstract static class Builder<T extends Data> {
private String one;
protected Builder() {
this.one = null;
}
public final Builder<T> withOne(final String value) {
this.one = value;
return this;
}
protected abstract T build();
}
private final String one;
protected Data(final Builder<? extends Data> builder) {
this.one = builder.one;
}
public final String getOne() {
return this.one;
}
}
The class is extended, which also includes its own extended Builder:
public final class Extension extends Data {
public static final class ExtensionBuilder extends Data.Builder<Extension> {
private String two;
private ExtensionBuilder() {
super();
this.two = null;
}
public static final ExtensionBuilder newInstance() {
return new ExtensionBuilder();
}
public final ExtensionBuilder withTwo(final String value) {
this.two = value;
return this;
}
public final Extension build() {
return new Extension(this);
}
}
private final String two;
private Extension(final ExtensionBuilder builder) {
super(builder);
this.two = builder.two;
}
public final String getTwo() {
return this.two;
}
}
The Extension object gets all methods from both classes. However the order the setter methods is called is important. This is OK:
Extension one = Extension.ExtensionBuilder
.newInstance()
.withTwo("two")
.withOne("one")
.build();
Whereas this produces a compilation error:
Extension two = Extension.ExtensionBuilder
.newInstance()
.withOne("one")
.withTwo("two")
.build();
I know the reason why, the withOne() setter has downcast the type of Builder object from the concrete class to the abstract one. I would like to know what I need to do to solve this, so setters can be called in any order.
The Builder Pattern is another creational design pattern that uses a Builder to create objects by assembling their parts. Builder pattern allows for the construction of complex objects by using a step-by-step process.
Implementation : In Builder pattern, we have a inner static class named Builder inside our Server class with instance fields for that class and also have a factory method to return an new instance of Builder class on every invocation. The setter methods will now return Builder class reference.
The Factory Method pattern is a creational design pattern that uses factories to create objects without specifying the exact type of object to be created. The Builder Pattern is another creational design pattern that uses a Builder to create objects by assembling their parts.
The Builder has the setter methods that construct the complex object. Typically, each method returns void. Only the Build method returns the type you are building. The Fluent builder pattern is a creational pattern that makes object creation more straightforward and readable.
If you don't want (or can't) override the withOne
method in the ExtensionBuilder
class as suggested in this answer, you can use a recursively bounded generic type for your builder:
abstract static class Builder<T extends Data, B extends Builder<T, B>> {
private String one;
protected Builder() {
this.one = null;
}
public final B withOne(final String value) {
this.one = value;
return (B) this;
}
protected abstract T build();
}
Then, declare the ExtensionBuilder
class as follows:
public static final class ExtensionBuilder
extends Data.Builder<Extension, ExtensionBuilder>
This will allow you to use the most concrete builder's methods in any order, because the compiler now knows the static type of the concrete builder.
EDIT:
As pointed out by @Radiodef in the comments, there's an alternative to the cast, which consists of declaring a protected abstract B getThis()
method in the Builder
class:
abstract static class Builder<T extends Data, B extends Builder<T, B>> {
private String one;
protected Builder() {
this.one = null;
}
protected abstract B getThis();
public final B withOne(final String value) {
this.one = value;
return getThis(); // no need to cast now
}
protected abstract T build();
}
And of course in the ExtensionBuilder
you should implement it as:
@Override
protected ExtensionBuilder getThis() { return this; }
Here's the source: http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ205
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With