我基于此answer实现了模式
我有以下抽象配置:

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;
    }
}


我有以下具体配置:

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;
    }
}


这就是我的使用方式

DialogConfig config = new DialogConfig.DialogConfigBuilder()
                .setWidth(0)
                .setCalories(0)
                .setHeight(0) //X LINE
                .build();


在X行,我得到-找不到符号方法setHeight。我怎么了

编辑-我将有一个ExtendedDialogConfig和一个必须扩展DialogConfig等的类。我的意思是,将会有其他子类。

最佳答案

您首先要将setCalories()更改为:

public Builder<B> setCalories(int calories) {
  this.calories = calories;
  return this;
}


摆脱那种偏见和警告。现在仔细看看。您返回一个生成器。这段代码不知道将来的子类。它仅返回该基本构建器的实例。

因此,当您进行链接呼叫时:

 .setHeight(0) .build();


那将返回那个基础建造者。然后调用build()-这将建立一个抽象配置。但是您想将其分配给更特定的DialogConfig。因此错误。

一个(丑陋的)解决方法:

DialogConfig.DialogConfigBuilder<?> builder = new DialogConfig.DialogConfigBuilder<>().setHeight(0);
builder.setCalories(0);

...config = builder.build();


还有一个解决方案-再次修改setCalories()

@SuppressWarnings("unchecked")
public <T extends B> T setCalories(int calories) {
  this.calories = calories;
  return (T) this;
}


修复编译错误;并允许链接setCalories()调用。剩下的摆脱演员/抑制的练习留给读者练习。

记录在案的“完整”解决方案,包括摆脱原始类型和其他警告的所有调整:

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);
    }
}

08-16 22:38