是否存在推断协方差
Does Inferred Covariance exist
我正在将一些代码重构到构建器模式中,并且 运行 解决了 sub-classing child class 的构建器问题。
当我有一个构建器子 class 时,我尝试在 parent 中链接一个方法,它返回 parent 构建器,所以我的实例不再具有访问权限child class 方法。
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 class 上,returns 是 ParentBuilder。重新排序链以调用所有 ChildBuilder 方法首先解决了问题,但对于使用我的 api(包括我自己)的人来说,这听起来像是一种糟糕的体验。
如果我在 child 中添加覆盖,我可以通过协方差使其工作:
@Override
public ChildBuilder withName(String name) {
super.withName(name);
return this;
}
但这是很多我不想维护的样板代码(每个 parent 构建器可能有一些子 classes,所以我需要在每个中重写这些方法parent class 中的每个方法的 subclass)。
有没有办法在没有覆盖的情况下完成我想做的事情? java "infer" children中的协变方法可以吗?
我也很担心,这个问题说明我设计的构建器不正确。
不,没有推断协方差,但它被奇怪的重复出现的模板模式(或起源于 C++ 的 CRTP)模仿。
您可以通过添加 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> {}
我正在将一些代码重构到构建器模式中,并且 运行 解决了 sub-classing child class 的构建器问题。
当我有一个构建器子 class 时,我尝试在 parent 中链接一个方法,它返回 parent 构建器,所以我的实例不再具有访问权限child class 方法。
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 class 上,returns 是 ParentBuilder。重新排序链以调用所有 ChildBuilder 方法首先解决了问题,但对于使用我的 api(包括我自己)的人来说,这听起来像是一种糟糕的体验。
如果我在 child 中添加覆盖,我可以通过协方差使其工作:
@Override
public ChildBuilder withName(String name) {
super.withName(name);
return this;
}
但这是很多我不想维护的样板代码(每个 parent 构建器可能有一些子 classes,所以我需要在每个中重写这些方法parent class 中的每个方法的 subclass)。
有没有办法在没有覆盖的情况下完成我想做的事情? java "infer" children中的协变方法可以吗?
我也很担心,这个问题说明我设计的构建器不正确。
不,没有推断协方差,但它被奇怪的重复出现的模板模式(或起源于 C++ 的 CRTP)模仿。
您可以通过添加 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> {}