I'm trying to have an abstract base class for some builder classes so I can easily reuse code between the Builder implementations. I want my builders to support method chaining therefore a method has to return "this" instance of the most specific type. I figured I could probably do this with generics. Unfortunatly I did not manage to do it without using unsafe operations. Is it possible?
Sample code of how I'm trying it (and how it works) below. I'd like to avoid casting to T in "foo()" (which causes an unchecked warning), can this be done?
public class Builders
{
public static void main( final String[] args )
{
new TheBuilder().foo().bar().build();
}
}
abstract class AbstractBuilder<T extends AbstractBuilder<?>>
{
public T foo()
{
// set some property
return (T) this;
}
}
class TheBuilder extends AbstractBuilder<TheBuilder>
{
public TheBuilder bar()
{
// set some other property
return this;
}
public Object build()
{
return new Object();
}
}
You want to declare T
as extends AbstractBuilder<T>
in AbstractBuilder
.
Use an abstract protected
method to get this
of type T
.
abstract class AbstractBuilder<T extends AbstractBuilder<T>> {
protected abstract T getThis();
public T foo() {
// set some property
return getThis();
}
}
class TheBuilder extends AbstractBuilder<TheBuilder> {
@Override protected TheBuilder getThis() {
return this;
}
...
}
Alternatively, drop the generic type parameter, rely on covariant return types and make the code cleaner for clients (although usually they would be using TheBuilder
rather than the largely implementation detail of the base class), if making the implementation more verbose.
One alternative is not to use generics, but use overrides:
abstract class AbstractBuilder
{
public AbstractBuilder foo()
{
// set some property
return this;
}
}
class TheBuilder extends AbstractBuilder
{
@Override public TheBuilder foo()
{
super.foo(); return this;
}
public TheBuilder bar()
{
// set some other property
return this;
}
public Object build()
{
return new Object();
}
}
This should help:
abstract class AbstractBuilder<T extends AbstractBuilder<?>>
{
public AbstractBuilder<T> foo()
{
// set some property
return (AbstractBuilder<T>) this;
}
abstract AbstractBuilder<T> bar();
abstract Object build();
}