Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/304.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/visual-studio-2012/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 是否存在推断协方差_Java_Oop_Subclass_Covariance - Fatal编程技术网

Java 是否存在推断协方差

Java 是否存在推断协方差,java,oop,subclass,covariance,Java,Oop,Subclass,Covariance,我正在将一些代码重构为构建器模式,并在为子类对构建器进行子分类时遇到了一个问题 当我有一个生成器子类,并且我尝试在父类中链接一个方法时,它将返回父类生成器,因此我的实例不再具有访问子类方法的权限 public class App { public static void main(String[] args) { Parent p; p = new App().new ChildBuilder() .withName("Test"

我正在将一些代码重构为构建器模式,并在为子类对构建器进行子分类时遇到了一个问题

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

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(即参数类型为子类)的(包私有)抽象类来解决这个问题。生成器功能将移动到这些类,然后创建两个扩展抽象生成器的空类

我还更改了构造函数,使其不直接依赖于生成器,而是依赖于实际参数,因为这通常是如何完成的,这使这里的实现更简洁:

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> {}
publicstaticvoidmain(字符串[]args){
//这两个例子现在都可以编译了
亲本p;
p=新应用程序()。新儿童生成器()
.withName(“测试”)
.昵称为(“测试1”)
.build();
p=新应用程序()。新儿童生成器()
.昵称为(“测试1”)
.withName(“测试”)
.build();
}
班级家长{
公共父项(字符串名称){}
}
类子级扩展父级{
公共子项(字符串名称,字符串昵称){super(名称);}
}
抽象类AbstractParentBuilder{
受保护的字符串名称;
受保护的AbstractParentBuilder(){}
公共父级生成(){返回新的父级(名称);}
@抑制警告(“未选中”)
带名称的公共T(字符串名称){
this.name=名称;
返回(T)这个;
}
}
类ParentBuilder扩展了AbstractParentBuilder{}
抽象类AbstractChildBuilder扩展了AbstractParentBuilder{
保护字符串昵称;
受保护的AbstractChildBuilder(){}
public Child build(){返回新的子级(名称、昵称);}
@抑制警告(“未选中”)
带昵称的公共T(字符串昵称){
this.昵称=昵称;
返回(T)这个;
}
}
类ChildBuilder扩展了AbstractChildBuilder{}

(在Java中,您通常会听到它被称为“自我类型模式”。)太棒了,谢谢。你有没有关于我也改变了构造函数的来源,使其不直接依赖于构建器,而是依赖于实际的参数,因为它通常是这样做的?我正在构建我的实现,主要是基于不同的实现。另外,如果我正确地阅读了泛型,那么我们正在配置父构建器,以便在运行时使用子构建器的类型进行实例化?@Chris Uh,我没有这方面的源代码,抱歉,这正是我记得大多数时候看到的(现在看到这条语句,有点像波浪状,抱歉)。在将构建器传递给构造函数时,如果您想选择使用单独接受参数的常规构造函数,则必须单独添加该构造函数。但是,如果需要,可以将
Parent
Child
的参数分别更改为
AbstractParentBuilder
AbstractChildBuilder