Reputation:
I implemented pattern based on this answer I have the following asbtract config:
public abstract class AbstractConfig {
public static abstract class Builder<B extends Builder<B>> {
private int calories = 0;
public Builder() {
}
public B setCalories(int calories) {
this.calories = calories;
return (B) this;
}
public abstract AbstractConfig build();
}
private int calories = 0;
protected AbstractConfig(final Builder builder) {
calories = builder.calories;
}
}
And I have the following concrete config:
public class DialogConfig extends AbstractConfig {
public static class DialogConfigBuilder<B extends DialogConfigBuilder<B>> extends Builder<B> {
private double width;
private double height;
public DialogConfigBuilder() {
//does nothing.
}
public B setWidth(final double value) {
width = value;
return (B) this;
}
public B setHeight(final double value) {
height = value;
return (B) this;
}
public DialogConfig build() {
return new DialogConfig(this);
}
}
private final double width;
private final double height;
protected DialogConfig(final DialogConfigBuilder builder) {
super(builder);
width = builder.width;
height = builder.height;
}
public double getWidth() {
return width;
}
public double getHeight() {
return height;
}
}
And this is how I use it
DialogConfig config = new DialogConfig.DialogConfigBuilder()
.setWidth(0)
.setCalories(0)
.setHeight(0) //X LINE
.build();
At X line I get - Can't find symbol method setHeight. What is my mistake?
EDIT - I will have and a ExtendedDialogConfig that must extend DialogConfig and etc. I mean there will be other subclasses.
Upvotes: 1
Views: 796
Reputation:
I found my mistake. This is how I used DialogConfigBuilder
DialogConfig config = new DialogConfig.DialogConfigBuilder()
.setWidth(0)
.setCalories(0)
.setHeight(0) //X LINE
.build();
This is how I should use DialogConfigBuilder
DialogConfig config = new DialogConfig.DialogConfigBuilder<>()
.setWidth(0)
.setCalories(0)
.setHeight(0) //X LINE
.build();
Pay attention to <>
in the second case.
Upvotes: 1
Reputation: 140633
You would first change setCalories()
to:
public Builder<B> setCalories(int calories) {
this.calories = calories;
return this;
}
to get rid of that cast and the warning. And now look at this closely. You return a Builder. This code doesn't know about future subclasses. It only returns an instance of that base builder.
As a consequence, when you have that chained call:
.setHeight(0) .build();
that would return that base builder. To then call build()
- which would build an abstract configuration. But you want to assign that to a more specific DialogConfig. Thus the error.
A (ugly) workaround:
DialogConfig.DialogConfigBuilder<?> builder = new DialogConfig.DialogConfigBuilder<>().setHeight(0);
builder.setCalories(0);
...config = builder.build();
And a solution - by again reworking setCalories()
:
@SuppressWarnings("unchecked")
public <T extends B> T setCalories(int calories) {
this.calories = calories;
return (T) this;
}
Fixes the compile error; and allows chaining the setCalories()
call as well. Final exercise of getting rid of the cast/suppress is left as exercise to the reader.
And for the record - the "complete" solution, including all adaptions to get rid of raw types and other warnings:
abstract class AbstractConfig {
public static abstract class Builder<B extends Builder<B>> {
private int calories = 0;
@SuppressWarnings("unchecked")
public <T extends B> T setCalories(int calories) {
this.calories = calories;
return (T) this;
}
public abstract AbstractConfig build();
}
private int calories = 0;
public int getCalories() { return calories; }
protected <B extends Builder<B>> AbstractConfig(final Builder<B> builder) {
calories = builder.calories;
}
}
final class DialogConfig extends AbstractConfig {
public static class DialogConfigBuilder<B extends DialogConfigBuilder<B>> extends Builder<B> {
private double width;
private double height;
public DialogConfigBuilder<B> setWidth(final double value) {
width = value;
return this;
}
public DialogConfigBuilder<B> setHeight(final double value) {
height = value;
return this;
}
public DialogConfig build() {
return new DialogConfig(this);
}
}
private final double width;
private final double height;
protected <B extends DialogConfigBuilder<B>> DialogConfig(final DialogConfigBuilder<B> builder) {
super(builder);
width = builder.width;
height = builder.height;
}
public double getWidth() { return width; }
public double getHeight() { return height; }
}
public class Builders {
public static void main(String[] args) {
DialogConfig config = new DialogConfig.DialogConfigBuilder<>().setHeight(0).setCalories(0).build();
System.out.println(config);
}
}
Upvotes: 1