如何定义构建器模式层次结构,可以按任何顺序调用setter

Mar*_*iam 7 java generics abstract-class builder

考虑带有抽象Builder的抽象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;
    }
}
Run Code Online (Sandbox Code Playgroud)

该类已扩展,其中还包括自己的扩展构建器:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

Extension对象从两个类中获取所有方法.但是,调用setter方法的顺序很重要.还行吧:

Extension one = Extension.ExtensionBuilder
                .newInstance()
                .withTwo("two")
                .withOne("one")
                .build();
Run Code Online (Sandbox Code Playgroud)

这会产生编译错误:

Extension two = Extension.ExtensionBuilder
                .newInstance()
                .withOne("one")
                .withTwo("two")
                .build();
Run Code Online (Sandbox Code Playgroud)

我知道为什么,withOne()setter将Builder对象的类型从具体类向下转换为抽象类.我想知道我需要做些什么来解决这个问题,因此可以按任何顺序调用setter.

Fed*_*ner 4

如果您不想(或不能)按照此答案withOne中的建议重写类中的方法,则可以为您的构建器使用递归有界泛型类型:ExtensionBuilder

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();
}
Run Code Online (Sandbox Code Playgroud)

然后,声明该类ExtensionBuilder如下:

public static final class ExtensionBuilder 
    extends Data.Builder<Extension, ExtensionBuilder>
Run Code Online (Sandbox Code Playgroud)

这将允许您以任何顺序使用最具体的构建器的方法,因为编译器现在知道具体构建器的静态类型。

编辑:正如@Radiodef 在注释中 指出的,还有一种替代方法,其中包括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();
}
Run Code Online (Sandbox Code Playgroud)

当然,ExtensionBuilder您应该将其实现为:

@Override
protected ExtensionBuilder getThis() { return this; }
Run Code Online (Sandbox Code Playgroud)

这是来源:http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ205

  • 可以使用“受保护的抽象 B getThis();”方法来避免未经检查的强制转换,如 Angelika Langer 的常见问题解答所述:http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ205 (2认同)