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:
- And finally ConcreteBuilder
Now we want to write client code that uses
ConcreteBuilder. The code we want to have is the following:
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
OK, so how do we suppose to fix the code. I know of 2 possible solutions:
- Use covariant return types and override methods from base classes
- Use recursive generics when defining builder classes
Covariant return types
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)
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.
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:
And the client code haven’t changed at all, it still compiles and runs as before:
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
E, which effectively tells Java compiler that method returns some subclass of
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
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
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.
Java’s generics can be real pain (especially when wildcards are used), but in some cases clever usage of generics can save your day!