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?
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.
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();
}
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();
}
}
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