Java generic builder

我的梦境 提交于 2019-12-09 15:53:39

问题


Suppose I need some DerivedBuilder to extend some BaseBuilder. Base builder has some method like foo (which returns BaseBuilder). Derived builder has method bar. Method bar should be invoked after method foo. In order to do it I can override foo method in DerivedBuilder like this:

@Override
public DerivedBuilder foo() {
    super.foo();
    return this;
}

The problem is that BaseBuilder has a lot of methods like foo and I have to override each one of them. I don't want to do that so I tried to use generics:

public class BaseBuilder<T extends BaseBuilder> {
    ...

    public T foo() {
        ...
        return (T)this;
    }
}

public class DerivedBuilder<T extends DerivedBuilder> extends BaseBuilder<T> {
    public T bar() {
        ...
        return (T)this;
    }
}

But the problem is that I still can not write

new DerivedBuilder<DerivedBuilder>()
        .foo()
        .bar()

Even though T here is DerivedBuilder. What can I do in order to not to override a lot of functions?


回答1:


Your problem is the definition of DerivedBuilder:

class DerivedBuilder<T extends DerivedBuilder>;

And then instantiating it with a type erased argument new DerivedBuilder<DerivedBuilder<...what?...>>().

You'll need a fully defined derived type, like this:

public class BaseBuilder<T extends BaseBuilder<T>> {
    @SuppressWarnings("unchecked")
    public T foo() {
        return (T)this;
    }
}

public class DerivedBuilder extends BaseBuilder<DerivedBuilder> {
    public DerivedBuilder bar() {
        return this;
    }
}

Check ideone.com.




回答2:


In addition to BeyelerStudios's answer, if you want to nest further, you can just use this:

class Base<T extends Base<?>> {
    public T alpha() { return (T) this; }
    public T bravo() { return (T) this; }
    public T foxtrot() { return (T) this; }
}

class Derived<T extends Derived<?>> extends Base<T> {
    public T charlie() { return (T) this; }
    public T golf() { return (T) this; }
}

class FurtherDerived<T extends FurtherDerived<?>> extends Derived<T> {
    public T delta() { return (T) this; }
    public T hotel() { return (T) this; }
}

class MuchFurtherDerived<T extends MuchFurtherDerived<?>> extends FurtherDerived<T> {
    public T echo() { return (T) this; }
}

public static void main(String[] args) {
    new MuchFurtherDerived<MuchFurtherDerived<?>>()
        .alpha().bravo().charlie().delta().echo().foxtrot().golf().hotel()
        .bravo().golf().delta().delta().delta().hotel().alpha().echo()
        .echo().alpha().hotel().foxtrot();
}



回答3:


Instead of casting return (T) this; I here did a Class.cast(this).

To realize:

BaseBuilder.build(ExtendedBuilder.class).foo().bar().foo().bar();

Every class in the hierarch needs to know the actual final child class, hence I chose to make a factory method build in the base class.

The cast of this to the actual child is done in a final method of the base class too, providing return me();.

class BaseBuilder<B extends BaseBuilder<B>> {

    protected Class<B> type;

    public static <T extends BaseBuilder<T>> T build(Class<T> type) {
        try {
            T b = type.newInstance();
            b.type = type;
            return b;
        } catch (InstantiationException | IllegalAccessException e) {
            throw new IllegalStateException(e);
        }
    }

    protected final B me() {
        return type.cast(this);
    }

    B foo() {
        System.out.println("foo");
        return me();
    }
}

class ExtendedBuilder extends BaseBuilder<ExtendedBuilder> {

    ExtendedBuilder bar() {
        System.out.println("bar");
        return me();
    }
}



回答4:


What I understand from your question is that the method foo() should be executed before method bar().
If that is correct, you can apply the Template Design Pattern.
Create a abstract method bar in the BaseBuilder.
And a new method say 'template'. The method template will define the sequence- first foo() is executed followed by bar().
DerivedBuilder will provide the implementation for the method bar.

public abstract class BaseBuilder {

    public void foo(){
        System.out.println("foo");
    }

    public abstract void bar();

    public void template(){
        foo();
        bar();
    }
}

public class DerivedBuilder extends BaseBuilder{

    @Override
    public void bar() {
        System.out.println("bar");      
    }

    public static void main(String[] args) {
        BaseBuilder builder = new DerivedBuilder();
        builder.template();
    }
}


Hope this helps.



来源:https://stackoverflow.com/questions/39015564/java-generic-builder

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!