使用超类创建生成器时,父类不能返回子类的实例



如果我使用构建器模式来配置新对象,我可能有两个类,如GameHockeyGame(如下所示)。当我想创建一个新的HockeyGame时,我得到了它的构建器并开始调用方法来根据需要配置对象。

我遇到的问题显示在主函数中。一旦我从超类调用一个方法,它就会返回为Game.Builder的意图,并且我不能再从子类调用任何方法。

处理这个问题的最佳方法是什么?

主.java

class Main {
    public static void main(String[] args){
        HockeyGame hg = new HockeyGame.Builder()
                .setScore(5)
                .setTimeLimit(3600)
//--------------------------------------------------------------------
                .setIceTemperature(-5) // Error! Cannot call setIceTempurature() on
                                       // an instance of Game.Builder
//--------------------------------------------------------------------
                .build();

    }
}

游戏.java

public class Game{
    int score;
    int timeLimit;
    public Game(int score, int timeLimit) {
        this.score = score;
        this.timeLimit = timeLimit;
    }
    public static class Builder {
        int score;
        int timeLimit;
        public Builder setScore(int score) {
            this.score = score;
            return this;
        }
        public Builder setTimeLimit(int timeLimit) {
            this.timeLimit = timeLimit;
            return this;
        }
        public Game build() {
            return new Game(score, timeLimit);
        }
    }
}

曲棍球游戏.java

public class HockeyGame extends Game {
    float iceTemperature;
    public HockeyGame(int score, int timeLimit, float iceTemperature) {
        super(score, timeLimit);
        this.iceTemperature = iceTemperature;
    }
    public static class Builder extends Game.Builder {
        float iceTemperature;
        public HockeyGame.Buidler setIceTemperature(float iceTemperature) {
            this.iceTemperature = iceTemperature;
            return this;
        }
        public HockeyGame build(){
            return new HockeyGame(score, timeLimit, iceTemperature);
        }
    }
}

谢谢。

您需要

使用在非常流畅的 API 代码中普遍存在的getThis()技巧。

首先,您需要使Game.Builder本身具有通用性:

public static class Builder<B extends Builder<B>>

然后添加一个getThis()方法:

public B getThis() {
    return (B) this;
}

现在你改变你的二传手,返回一个Breturn getThis(),而不是this

public B setTimeLimit(int timeLimit) {
    //...
    return getThis();
}

扩展类本身也需要是泛型的:

public static class Builder<B extends Builder<B>> extends Game.Builder<B>

现在您可以使用代码,它将"记住"预期的类型:

HockeyGame hockeyGame = new HockeyGame.Builder<>().setScore(10)
        .setTimeLimit(20)
        .setIceTemperature(-1)
        .build();

最终代码如下所示:

public class Game {
    private final int score;
    private final int timeLimit;
    private Game(final Builder<?> builder) {
        this.score = builder.score;
        this.timeLimit = builder.timeLimit;
    }
    public static class Builder<B extends Builder<B>> {
        private int score;
        private int timeLimit;
        public B setScore(int score) {
            this.score = score;
            return getThis();
        }
        public B setTimeLimit(int timeLimit) {
            this.timeLimit = timeLimit;
            return getThis();
        }
        protected B getThis() {
            return (B) this;
        }
        public Game build() {
            return new Game(this);
        }
    }
}
public class HockeyGame extends Game {
    private final float iceTemperature;
    private HockeyGame(final Builder<?> builder) {
        super(builder);
        this.iceTemperature = builder.iceTemperature;
    }
    public static class Builder<B extends Builder<B>> extends Game.Builder<B> {
        private float iceTemperature;
        public B setIceTemperature(float iceTemperature) {
            this.iceTemperature = iceTemperature;
            return getThis();
        }
        @Override
        public HockeyGame build() {
            return new HockeyGame(this);
        }
    }
}

注意:我已经将字段private final,并且还制作了主要类型构造函数 - 这迫使人们使用Builder。 此外,构造函数可以采用Builder<?>并从那里复制变量 - 这会稍微整理代码。


正如您可能已经注意到的那样,实际的黑客在这里:

public B getThis() {
    return (B) this;
}

在这里,我们强制将Builder转换为其泛型类型 - 这允许我们根据所使用的特定实例更改方法的返回类型。 问题是,如果您声明一个新Builder如下所示的内容:

public static class FootballGame extends Game {
    private FootballGame(final Builder<?> builder) {
        super(builder);
    }
    public static class Builder<B extends HockeyGame.Builder<B>> extends Game.Builder<B> {
        float iceTemperature;
        @Override
        public FootballGame build() {
            return new FootballGame(this);
        }
    }
}

这将在运行时以ClassCastException爆炸.但是setter方法将返回HockeyGame.Builder而不是FootballGame.Builder因此问题应该是显而易见的。

试试这样的事情

您可以显式地将其强制转换为HockeyGame.Builder对象,并在其上使用自己的方法。

您遇到的问题是setTimeLimit返回一个Builder对象(母类),因此您不能对其使用子方法。

HockeyGame hg = ((HockeyGame.Builder)(new HockeyGame.Builder().setScore(5)
                                                              .setTimeLimit(3600)))
                                                              .setIceTemperature(-5)
                                                              .build();

此外,setIceTemparature应该返回一个HockeyGame.Builder对象,以便能够对其进行build

public Builder setIceTemperature(float iceTemperature) {
    this.iceTemperature = iceTemperature;
    return this;
}

最新更新