Recursive generics to the rescue

In this post I will show how something as non-intuitive (read ugly) as recursive generics can help to solve problem of creating extendable Builders.

But first we need to define what are we trying to achieve. We need to build set of Builder objects that facilitate creation of non-trivial domain objects and provide fluent interface (for the definition of fluent interface see Martin Fowler’s article) for user along the lines.

So let’s start with defining our 3 builder classes:

  • BaseBuilder
    BaseBuilder class
  • ConstraintBuilder
    ConstraintBuilder class
  • And finally ConcreteBuilder
    ConcreteBuilder class

Now we want to write client code that uses ConcreteBuilder. The code we want to have is the following:
Client code

Notice however that this code has compilation error:

"The method equalTo(int) is undefined for the type BaseBuilder".

Java compiler tells us that we are trying to invoke method on the class BaseBuilder whereas we are assuming to be working with class ConcreteBuilder.

OK, so how do we suppose to fix the code. I know of 2 possible solutions:

  1. Use covariant return types and override methods from base classes
  2. Use recursive generics when defining builder classes

Covariant return types

From wikipedia:

In object-oriented programming, a covariant return type of a method is one that can be replaced by a “narrower” type when the method is overridden in a subclass.

Thus knowing this we can fix our builders by overriding methods from parent classes and forcing them to return instance of the leaf class instead. So here is how our classes end up looking in the end:

  • BaseBuilder (not changed)
    BaseBuilder class
  • ConstraintBuilder
    ConstraintBuilder class
  • ConcreteBuilder
    ConcreteBuilder class

With these changes in place our client code now compiles and runs, but we had to do too much work in my opinion. Notice for instance that we had to override all methods from base classes in each and every derived class.

We are forced to do this whenever subclass adds new methods, cause we need to provide end user with flexibility of invoking methods in any order irrespective of where they are defined.
Also in this example it was not necessary to do this for ConcreteBuilder class but I showed it here for completeness, mainly to stress how much involved first approach is.

Enough for the ugliness, so let’s checkout second approach.

Recursive generics

OK, so is there better way to achieve the same goal without repeating same steps for each builder subclass? The answer is yes.

We can use generics while defining our builders to tell Java that return type of methods is not the builder’s class but rather the subclass of the builder, hence recursive generic definition.

Let’s see what it means for our builders:

  • BaseBuilder
    BaseBuilder class
  • ConstraintBuilder
    ConstraintBuilder class
  • ConcreteBuilder
    ConcreteBuilder class

And the client code haven’t changed at all, it still compiles and runs as before:
Client code

Let’s have a quick recap of what we just did. We changed definition of the BaseBuilder class to contain recursive generic parameter E (i.e. BaseBuilder<E extends BaseBuilder>). This allowed us change return type of the methods from BaseBuilder to E, which effectively tells Java compiler that method returns some subclass of BaseBuilder class.

Then we did the same for ConstraintBuilder class (i.e. ConstraintBuilder<E extends ConstraintBuilder>) which again tells javac that return type is some subclass of ConstraintBuilder class.

And finally we stopped recursion for class ConcreteBuilder by specifying itself as the generic parameter while extending ConstraintBuilder class (i.e. ConcreteBuilder extends ConstraintBuilder<ConcreteBuilder>). Since this class is at the bottom of our inheritance hierarchy we could stop using generic parameter. However if it would have subclasses on it’s own then we would have to repeat declaration similar to that of ConstraintBuilder class.

Notice how much less effort was to implement second solution. Despite of some minor noise such as usage of @SuppressWarnings("unchecked") annotation to eliminate compilation warnings and casts to E this solution is much cleaner, easier to understand and maintain. There is no code duplication that we saw in first solution and it scales well when the inheritance hierarchy is getting bigger and deeper.

To conclude:
Java’s generics can be real pain (especially when wildcards are used), but in some cases clever usage of generics can save your day! ;-)

About these ads

6 responses to “Recursive generics to the rescue

  • Rabea

    Thank you for this post Dmitry, needed it today :)

  • જીગર બ્રહ્મભટ્ટ (Jigar Brahmbhatt)

    Hey Dmitri, what to do if I have one more level of class hierarchy and I want to build both the leaf class and its parent class ?

    In that case, I can’t make the leaf class’ parent class as abstract.

    I might have to mix-n-match generics up to 2 level and then use covariant return types for the 3rd level leaf node ?

    Any better solution than this ?


    • Alex

      In a most strict approach, all classes should be either “final” or “abstract,” and thus you’re hierarchy of A <- B <- C would include a fourth class A <- B <-D where C and D are the classes you want to instantiate, and they share the common abstract base class B. It is a bit out of the way, but it is more maintainable in the long run.

  • Alex

    Get rid of the @suppressWarnings(“unchecked”) and unchecked casts to “E” by adding a method:

    protected abstract E self();

    to BaseBuilder and have the derived class implement it as follows:

    protected ConstraintBuilder self() { return this; }

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Latency Tip Of The Day

"Nothing is more dangerous than an idea when it is the only one you have." (Emile Chartier)

Psychosomatic, Lobotomy, Saw

"Nothing is more dangerous than an idea when it is the only one you have." (Emile Chartier)

"Nothing is more dangerous than an idea when it is the only one you have." (Emile Chartier)

Mechanical Sympathy

"Nothing is more dangerous than an idea when it is the only one you have." (Emile Chartier)


Get every new post delivered to your Inbox.

%d bloggers like this: