考虑带有抽象构建器的抽象Data类:
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;
}
}
该类已扩展,其中还包括自己的扩展构建器:
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;
}
}
Extension对象从两个类中获取所有方法。但是,调用setter方法的顺序很重要。这没关系:
Extension one = Extension.ExtensionBuilder
.newInstance()
.withTwo("two")
.withOne("one")
.build();
这会产生编译错误:
Extension two = Extension.ExtensionBuilder
.newInstance()
.withOne("one")
.withTwo("two")
.build();
我知道为什么,withOne()setter将Builder对象的类型从具体类向下转换为抽象类。我想知道我需要做些什么才能解决这个问题,因此可以按任何顺序调用setter。
答案 0 :(得分:4)
如果您不希望(或不能)按照this answer中的建议覆盖withOne
班级中的ExtensionBuilder
方法,则可以使用recursively bounded generic type你的建设者:
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();
}
然后,按如下方式声明ExtensionBuilder
类:
public static final class ExtensionBuilder
extends Data.Builder<Extension, ExtensionBuilder>
这将允许您以任何顺序使用最具体的构建器方法,因为编译器现在知道具体构建器的静态类型。
修改强>
正如@Radiodef in the comments所指出的,有一个替代演员,其中包括在protected abstract B getThis()
类中声明Builder
方法:
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();
}
当然,在ExtensionBuilder
中,您应该将其实现为:
@Override
protected ExtensionBuilder getThis() { return this; }
以下是来源:http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ205
答案 1 :(得分:3)
问题是Builder
的{{1}}方法返回withOne
,而不是Builder<T>
,因此编译器没有看到{{1}方法。
您需要覆盖ExtensionBuilder
,因此它具有相同的功能,但会返回withTwo
类型的内容。
首先,在withOne
中设置ExtensionBuilder
而不是 withOne
,以便您可以在final
中覆盖它。
然后,覆盖它。只需调用重写的方法并返回Builder
即可保留相同的功能。
ExtensionBuilder
答案 2 :(得分:3)
你正在混淆两个完全不同的东西。 Builder模式是关于将对象创建与对象属性的规范分离。您的问题根本不是,而是使用方法链作为实施策略。为了证明这一点,我观察到通过转储方法链接,当前设计可以实现您喜欢的构建器方法调用的顺序:
Extension.ExtensionBuilder builder = Extension.ExtensionBuilder.newInstance();
builder.withOne("one");
builder.withTwo("two");
Extension two = builder.build();
方法链一直存在争议。 Streams API最近给了它一些积极的思想分享,但这并不适合所有情况。其中一个问题确实与你所描述的完全一致:它与继承不能很好地协调。