When I find something interesting and new, I post it here - that's mostly programming, of course, not everything.

Friday, July 15, 2011

Chain Calls in Java - a better solution

A while ago I wrote "Java Solutions: inheriting chain calls"; it was about what to do if we want to make it possible to write calls like
return new StringBuffer().append("Now is ").append(new Date()).append(", and ticking...").toString();


and be able to subclass at the same time.

See, the problem is that if you setter returns this:

  MyBaseEntity withExcitingNewFeature(ExcitingNewFeature f) {
    features.add(f);
    return this;
  }


and then you subclass MyBaseEntity, like MyDerivedEntity extends MyBaseClass, you won't be able to write
  MyDerivedEntity mde = new MyDerivedEntity(entityId).withExcitingNewFeature(feature);


Since, well, the class is different.

So in my previous post I suggested to make MyBaseEntity type-dependent (that is, have a generic):
 public abstract class MyBaseEntity<T extends MyBaseEntity> 


and declare a method
   abstract T self();


So that every subclass declaration would look like this:
  public class MyDerivedEntity extends MyBaseEntity<MyDerivedEntity>


and would define

  MyDerivedEntity self() {
    return this;
  }


Yes, it works. And probably for 2008, with is now ancient history, when slow people slowly typed their slow code, not caring about the speed of development... in short, it was long ago.

Now there's a better solution. No need to redefine self() in all possible subclasses. That was just stupid. The only place where it should be defined is the base class:

  T self() {
    return (T) this;
  }


And no need to declare MyBaseEntity abstract.

There's also a bug in that old post; the generic type should have been bounded, as Ran noted in his comment.

Of course all setters should look like this:
  <T extends MyBaseEntity> T withExcitingNewFeature(ExcitingNewFeature f) {
    features.add(f);
    return self();
  }

2 comments:

Sassa said...

You have a problem in this design. Try inheriting from MyDerivedEntity - let's call it MyDerivedDerivedEntity. What type is self()?

Denis said...

Should not declaration "MyBaseEntity withExcitingNewFeature(ExcitingNewFeature f)" be "T withExcitingNewFeature(ExcitingNewFeature f)"?

Followers

Subscribe To My Podcast

whos.amung.us