How do I define a builder pattern hierarchy where the setters can be called in any order

Marcus MacWilliam :

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.

Federico Peralta Schaffner :

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

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=460608&siteId=1