Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java generic builder

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?

like image 906
PepeHands Avatar asked Aug 18 '16 10:08

PepeHands


2 Answers

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.

like image 182
BeyelerStudios Avatar answered Oct 16 '22 10:10

BeyelerStudios


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();
}
like image 5
MC Emperor Avatar answered Oct 16 '22 10:10

MC Emperor