当创建具有超类的构建器时,父代无法返回子类的实例 [英] When creating a builder with a superclass, parent cannot return instance of child class
问题描述
如果我使用构建器模式来配置新对象,我可能会有两个类,例如 Game
和 HockeyGame
如下所示)。当我想创建一个新的 HockeyGame
时,我得到它的构建器,并根据需要开始调用方法来配置对象。
我正在运行的问题显示在主函数中。一旦我从超类中调用了一个方法,它返回为 Game.Builder
的意图,我不能再调用子类的任何方法。
有什么最好的方法来处理?
Main.java
public $ {
public static void main(String [] args){
HockeyGame hg = new HockeyGame.Builder )
.setScore(5)
.setTimeLimit(3600)
// ------------------------- -------------------------------------------
.setIceTemperature( -5)//错误!不能调用
上的setIceTempurature()// // Game.Builder的一个实例
// -------------------------- ------------------------------------------
.build() ;
}
}
Game.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;
返回这个;
}
public Builder setTimeLimit(int timeLimit){
this.timeLimit = timeLimit;
返回这个;
}
public Game build(){
return new Game(score,timeLimit);
}
}
}
HockeyGame.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扩展Game.Builder {
float iceTemperature;
public HockeyGame.Buidler setIceTemperature(float iceTemperature){
this.iceTemperature = iceTemperature;
返回这个;
}
public HockeyGame build(){
return new HockeyGame(score,timeLimit,iceTemperature);
}
}
}
谢谢。
您需要使用流畅的API中普遍的 getThis()
首先,您需要在自己中使您的 Game.Builder
generic
public static class Builder< B extends Builder< B>>
然后你添加一个 getThis()
:
public B getThis(){
return(B)this;
}
现在您更改设置器以返回 B
和返回getThis()
而不是这个
:
public B setTimeLimit(int timeLimit){
// ...
return getThis();
}
您的扩展类本身也需要泛型:
public static class Builder< B extends Builder< B>>扩展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;
私人游戏(最终生成器<?> 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>>扩展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>>扩展Game.Builder< B> {
float iceTemperature;
@Override
public FootballGame build(){
return new FootballGame(this);
}
}
}
这样会爆炸运行时带有 ClassCastException
。但是, setter
方法将返回一个 HockeyGame.Builder
而不是 FootballGame.Builder
所以问题应该是显而易见的。
If I am using the builder pattern to configure new objects I may have two classes like Game
and HockeyGame
(shown below). When I want to create a new HockeyGame
, I get it's builder and start calling methods to configure the object as needed.
The problem I am running into is shown in the main function. Once I call one method from the super class it returns as an intance of Game.Builder
, and I can no longer call any method from the child class.
What is the best way to deal with this?
Main.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();
}
}
Game.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);
}
}
}
HockeyGame.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);
}
}
}
Thanks.
You need to use the getThis()
trick that is prevalent in much fluent API code.
First you need to make your Game.Builder
generic in itself:
public static class Builder<B extends Builder<B>>
Then you add a getThis()
method:
public B getThis() {
return (B) this;
}
Now you change your setters to return a B
and return getThis()
rather than this
:
public B setTimeLimit(int timeLimit) {
//...
return getThis();
}
Your extension class also needs to be generic, in itself:
public static class Builder<B extends Builder<B>> extends Game.Builder<B>
Now you can use the code, and it will "remember" the intended type:
HockeyGame hockeyGame = new HockeyGame.Builder<>().setScore(10)
.setTimeLimit(20)
.setIceTemperature(-1)
.build();
This final code looks something like:
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);
}
}
}
N.B: I have made the fields private final
and also the main type constructors - this forces people to use the Builder
. Also, the constructor can take a Builder<?>
and copy the variable from there - this tidies the code a little.
The actual hack is, as you may have noticed, here:
public B getThis() {
return (B) this;
}
Here, we force a cast of the Builder
to its generic type - this allows us to change the return type of the method dependant upon the specific instance being used. The issue is, if you declare a new Builder
something like the following:
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);
}
}
}
This this will blow up at runtime with a ClassCastException
. But the setter
method will return a HockeyGame.Builder
rather than FootballGame.Builder
so the issue should be obvious.
这篇关于当创建具有超类的构建器时,父代无法返回子类的实例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!