将构建器扩展/子类化为直接子项

时间:2016-03-16 07:25:41

标签: java generics polymorphism subclass builder

我有三个班级

abstract class Animal {...}
class Shark extends Animal {...}
class Hammerhead extends Shark {...}

这些类中的每一个都有一个静态的内部_Builder类。这些是标准构建器类,但它们彼此扩展以允许在实例化时完全配置对象。例如构建器的使用看起来像

Hammerhead.getBuilder().setColor(color.GREY).setFinSize(1.3f).setHammerSize(.9f).build();

将返回Hammerhead

的新实例

最初我有这个问题,上面的内容不起作用,因为例如setColor()将返回一个 Animal.ABuilder ,我无法轻易地调用setFinSize()(它是 Shark.Sbuilder )的方法。

这可以通过将 _Builder 变成泛型来解决,如the answer to this question

中所示

这个答案适用于扩展一个孩子,但我遇到的问题不仅限于孙子建设者。

所以这里是构建器的类签名,因为我有它们 static class ABuilder<B extends ABuilder<B>>{...}
static class SBuilder<B extends SBuilder<B>> extends ABuilder<B extends ABuilder<B>>{...}
static class HBuilder<B extends HBuilder<B>> extends SBuilder<B extends SBuilder<B>>{...}

不幸的是,这给了我编译错误。

  

类型参数'B'不在其范围内;应该延长   'Animal.ABuilder'

此错误针对SBuilder定义中的第三个也是最后一个B

有没有办法实现这个目标?

谢谢!

来源
Animal.java

package com.example;

public abstract class Animal {

    String color;

    public Animal(ABuilder builder){
        this.color = builder.color;
    }

    public static class ABuilder<B extends ABuilder<B>> {
        String color;
        public B setColor(String c){
            color = c;
            return (B)this;
        }
    }
}

Shark.java

package com.example;

public class Shark extends Animal {

    float fin_size;

    public Shark(SBuilder builder){
        super(builder);
        fin_size = builder.fins;
    }

    //                                                 Problems are in this general area: \/
    public static class SBuilder<B extends SBuilder<B>> extends ABuilder<B extends ABuilder<B>>{ 

        float fins;

        public B setFinSize(float size){
            this.fins = size;
            return (B) this;
        }

        public Shark build(){
            return new Shark(this);
        }
    }
}

Hammerhead.java

package com.example;

public class Hammerhead extends Shark {

    float hammer;

    public Hammerhead(HBuilder builder) {
        super(builder);
        hammer = builder.hammer;
    }

    public static HBuilder<? extends  HBuilder> getBuilder(){
        return new HBuilder<HBuilder>();
    }

      public static class HBuilder<B extends HBuilder<B>> extends SBuilder<B extends SBuilder<B>> { {

        float hammer;

        public B setHammerSize(float hammer) {
            this.hammer = hammer;
            return (B)this;
        }

        public Hammerhead build(){
            return new Hammerhead(this);
        }
    }
}

修改

根据Seelenvirtuose的回答:

你的回答对我有意义。我以为我已经解决了问题,但是现在我已经进行了更改,但仍然没有按预期工作:

在第一次调用setColor()之后,返回的类型(由Intellij IDEA报告)就是函数定义的任何类。

每行的注释是函数调用返回的类型

Hammerhead hh =
    Hammerhead.getBuilder() // HBuilder<? extends HBuilder> //
        .setColor("Black") // ? extends com.example.Hammerhead.HBuilder
        .setHammerSize(10f) // HBuilder
        .setFinSize(10f) // SBuilder
        .setColor("Fuschia") // ABuilder

        .setFinSize(10f) // Error: No such method on ABuilder

        .build(); // Also does not work because build()
                  // should be called on an HBuilder

编辑2

解决方案有效。经过一番蹒跚学步后,我意识到我的getBuilder()方法遇到了第二个问题,导致了进一步的问题。当我回到家时,我将编辑上面的内容以反映正确的功能和解决方案。

最终修改

getBuilder函数应该是

    public static HBuilder<? extends  HBuilder<?>> getBuilder(){
        return new HBuilder<>();
    }

1 个答案:

答案 0 :(得分:1)

那么,您的Animal类具有以下构建器类:

public static class ABuilder<B extends ABuilder<B>> { ... }

类型参数B的目的是将ABuilder的任何子类型绑定到此类型变量。但是你不能在子类中正确使用这种子类型绑定。

只需将您的鲨鱼生成器和锤头生成器更改为:

public static class SBuilder<B extends SBuilder<B>> extends ABuilder<B> { ... }

public static class HBuilder<B extends HBuilder<B>> extends SBuilder<B> { ... }

由于B已经分别扩展SBuilderHBuilder),因此无需扩展ABuilder<B>以外的任何内容。