是否存在推断协方差



我正在将一些代码重构为构建器模式,并在为子类子类化构建器时遇到问题。

当我有一个生成器子类,并且我尝试在父类中链接一个方法时,它会返回父生成器,因此我的实例不再有权访问子类方法。

public class App {
public static void main(String[] args) {
Parent p;
p = new App().new ChildBuilder()
.withName("Test")
.withNickName("Test1")
.build();// Doesn't Compile
p = new App().new ChildBuilder()
.withNickName("Test1")
.withName("Test")
.build();
}
class Parent {
public Parent(ParentBuilder builder) {}
}
class Child extends Parent {
public Child(ChildBuilder builder) { super(builder); }
}
class ParentBuilder {
private String name;
public ParentBuilder() {}
public Parent build() { return new Parent(this); }
public ParentBuilder withName(String name) { 
this.name = name; 
return this; 
}
}
class ChildBuilder extends ParentBuilder {
private String nickName;
public ChildBuilder withNickName(String nickName) { 
this.nickName = nickName; 
return this; 
}
}
}  

main 方法中的第二行不会编译,因为withName("Test")位于 ParentBuilder 类上并返回 ParentBuilder。 重新排序链以调用所有 ChildBuilder 方法首先解决了这个问题,但对于使用我的 api 的人来说,这听起来像是一种糟糕的经历(包括我自己(。
如果我在子项中添加覆盖,我可以通过协方差使其工作:

@Override
public ChildBuilder withName(String name) { 
super.withName(name); 
return this; 
}

但是这是很多我宁愿不维护的样板代码(每个父生成器可以有几个子类,所以我需要为每个父类中的每个方法在每个子类中重写这些方法(。

有没有办法在没有覆盖的情况下做我想做的事情? Java可以"推断"子项中的协变方法吗?

我也担心这个问题表明我设计了不正确的建筑商。

不,没有推断的协方差,但它被奇怪的重复模板模式(或 CRTP,起源于 C++(模仿。

您可以通过添加 2 个使用 CRTP 的(包私有(抽象类(即参数类型是子类(来解决此问题。生成器功能将移动到这些类,然后创建 2 个扩展抽象生成器的空类。

我还更改了构造函数,使其不直接依赖于构建器,而是依赖于实际参数,因为通常是这样做的,并且它使这里的实现更干净一些:

public static void main(String[] args) {
// Both examples now compile
Parent p;
p = new App().new ChildBuilder()
.withName("Test")
.withNickName("Test1")
.build();
p = new App().new ChildBuilder()
.withNickName("Test1")
.withName("Test")
.build();
}
class Parent {
public Parent(String name) {}
}
class Child extends Parent {
public Child(String name, String nickName) { super(name); }
}
abstract class AbstractParentBuilder<T extends AbstractParentBuilder<T>> {
protected String name;
protected AbstractParentBuilder() {}
public Parent build() { return new Parent(name); }
@SuppressWarnings("unchecked")
public T withName(String name) {
this.name = name; 
return (T) this; 
}
}
class ParentBuilder extends AbstractParentBuilder<ParentBuilder> {}
abstract class AbstractChildBuilder<T extends AbstractChildBuilder<T>> extends AbstractParentBuilder<T> {
protected String nickName;
protected AbstractChildBuilder() {}        
public Child build() { return new Child(name, nickName); }
@SuppressWarnings("unchecked")
public T withNickName(String nickName) { 
this.nickName = nickName; 
return (T) this; 
}
}
class ChildBuilder extends AbstractChildBuilder<ChildBuilder> {}

相关内容

  • 没有找到相关文章

最新更新