从父类返回子类

2022-09-04 22:34:20

我有一个生成器类,它从大多数方法返回自身,以允许菊花链。为了使这适用于子类,我希望父方法返回子方法的实例,以便子方法可用于链接到末尾。

public class BaseBuilder<T extends BaseBuilder<T>> {
    public T buildSomething() {
        doSomeWork();
        /* OPTION #1: */ return this;     // "Type mismatch: cannot convert from BaseBuilder<T> to T"
        /* OPTION #2: */ return T.this;   // "Type mismatch: cannot convert from BaseBuilder<T> to T"
        /* OPTION #3: */ return (T) this; // "Type safety: Unchecked cast from SqlBuilder<T> to T"
    }
}

public class ChildBuilder extends BaseBuilder<ChildBuilder> {}

选项 #1 和 #2 导致编译错误,选项 #3 导致警告(尽管可以使用 来抑制)。这里有更好的方法吗?如何安全地将基础建设者降级为儿童建筑商?@SuppressWarnings("unchecked")


答案 1

该声明以某种方式指示代码异味,并且似乎违反了 DRY。在此示例中,只能使用参数化,而不能使用其他参数化,因此它应该是多余的。ChildBuilder extends BaseBuilder<ChildBuilder>BaseBuilderChildBuilder

我宁愿重新思考我是否真的想过度构建它,我会尝试将儿童构建器的所有方法放入.然后我可以简单地从所有支持链接的方法返回。BaseBuilderthis

如果我仍然认为通过将特定的生成器方法组分离到它们自己的类中会受益,那么我会优先考虑组合,因为不建议仅将继承应用于代码重用。

假设我们有两个子类:BaseBuilder

class BuilderA extends BaseBuilder<BuilderA> {
   BuilderA buildSomethingA() { return this; }
}

class BuilderB extends BaseBuilder<BuilderB> {
   BuilderB buildSomethingB() { return this; }
}

如果需要链接并喜欢,该怎么办:buildSomethingAbuildSomethingB

builder.buildSomething().buildSomethingA().buildSomethingB();

如果不将子类方法移动到 ;但是想象一下,这些方法也毫无意义,不应该从.BaseBuilderBuilderCBaseBuilder

如果我们仍然将这两个方法移动到超类,并且下次有三个其他方法,下次......我们最终会得到一个超类,负责整个层次结构的90%的职责,并有大量的代码,例如:

if ((this instanceof BuilderB) && !flag1 && flag2) {
   ...
} else if ((this instanceof BuilderC) && flag1 && !flag2 && thing != null) {
   ...
} else if ...

我更喜欢的解决方案是DSL,如:

builder.buildSomething1().buildSomething2()
   .builderA()
      .buildSomethingA1().buildSomethingA2()
   .end()
   .buildSomething3()
   .builderB()
      .buildSomethingB()
   .end();

Here 返回实例,以便您可以链接其更多方法或启动新的子生成器。end()builder

通过这种方式,(子)构建器可以从他们需要的任何内容中继承(否则他们必须仅扩展),并且可以拥有自己有意义的层次结构或组合。BaseBuilder


答案 2

在选项 #3 中强制转换是不安全的,因为以下类将编译(这是开发人员的责任):

public class ChildBuilder extends BaseBuilder<FakeBuilder> {}
                                              ^^^^^^^^^^^

一个常见的解决方案是向子类询问它们的:this

public abstract class BaseBuilder<T extends BaseBuilder<T>> {
  protected abstract T getThis();
  public T buildSomething() {
    return getThis();
  }
}

public class ChildBuilder extends BaseBuilder<ChildBuilder> {
  @Override
  protected ChildBuilder getThis() {
    return this;
  }
}