Re: Chained call pattern with inheritance, polymorphism and generics...
Daniel Pitts wrote:
The only way
I could see to cleanly handle this, is to make a generic abstract
getThis() method that has to be implemented in all the concrete types.
Since your method chaining is based on "this" only, there is no need for
"getThis()" at all.
I would probably simply it casting "this" to T in return of each
/chainable method/ of BaseBuilder, together with suppressing all
unchecked warnings on a class' level. Eventually, to allow the compiler
check other warnings, I would do a single final utility method (private
or protected) performing that cast:
@SuppressWarnings("unchecked")
protected final T thisAsT() {
return (T) this;
}
This gets even more complicated if I have several levels of
inheritance, including some sub-classes of already concrete types.
While "this" is either a type specified by T, or a subclass of T, I see
nothing wrong with inheritance here. Of course, in the latter case,
BaseBuilder's methods are unable to return "this" casted to an actual
object's type. But subclasses are still able to do so when you'd like
to preserve the ability of chaining them as well:
public class BobsChildBuider extends BodsBuilder {
@Override
public BobsChildBuilder something(Something<BodsBuilder> something) {
return (BobsChildBuilder) super.something(something);
}
}
Of course, to make it useful, you have to override all chainable
methods. So it's rather impractical, and likely most of direct
subclasses of a BaseBuilder should be final, or abstract (with passing
some of theirs own type parameters to the base class).
The only problem I can see here is with classes like the following:
public class OtherBuilder extends BaseBuilder<BobsBuilder> { ...
And, in general, there is no way to prevent someone from doing that.
But that's a different scenario than mentioned by you, in which your
original approach is better (of course "getThis()" is definitely not a
right name in such a case, "getT()", or other context depended name
might be used instead).
In your scenario classes extending a BaseBuilder must take care of this
problem by themselves.
BTW -- there are also the RFCs closely related to your problem:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6373386
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6479372
HTH,
piotr