编程语言
首页 > 编程语言> > java-推断协方差是否存在

java-推断协方差是否存在

作者:互联网

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

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

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可以在子级中“推断”协变方法吗?

我也担心这个问题表明我在错误地设计建造者.

解决方法:

不,没有推断出的协方差,但是它被奇怪的重复模板模式(或源自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> {}

标签:oop,subclass,covariance,java
来源: https://codeday.me/bug/20191109/2010911.html