Extendable Builders in Java

3 min read Original article ↗

Posted: February 23, 2012 | Filed under: java, programming | Tags: , |

So a while ago, I was pretty proud of myself for creating some method-chaining builders to build threads for running a data migration at work. At some point, I tried to extend the base builder for some reason or another. Trouble is, this breaks method-chaining (which is my favorite part). To see why, suppose we have a builder class for making Person objects:

public class PersonBuilder {
  private int height;
  private int weight;

  public PersonBuilder height(int height) {
    this.height = height;
    return this;
  }

  public PersonBuilder weight(int weight) {
    this.weight = weight;
    return this;
  }

  public Person buildPerson() {
    Person person = new Person();
    person.setHeight(height);
    person.setWeight(weight);
    return person;
  }
}

Then suppose we want to make a builder just for building Wrestlers:

public class WrestlerBuilder extends PersonBuilder {

  private String stageName;

  public WrestlerBuilder stageName(String stageName) {
    this.stageName = stageName;
    return this;

  @Override
  public Person buildPerson() {
    Wrestler wrestler = new Wrestler(super.buildPerson());
    wrestler.setStageName(stageName);
    return wrestler;
  }
}

This seems all fine and good until we try to do something like:

new WrestlerBuilder()
  .height(42)
  .weight(50)
  .stageName("Mucha Lucha")
  .buildPerson()

That code doesn’t compile. The problem is that the height and weight methods’ return type is PersonBuilder, which doesn’t know anything about stageName. Given all the cool and powerful frameworks that use method-chaining builders (Hibernate’s criteria queries, Camel’s route builders, and Solr’s Java client, to name some I’ve worked with), Java should make creating them as easy as possible. At the time I thought that there should be a primitive indicating that a method returns the class of the current object, but that’s problematic, and there’s a way to do what we want with generics. Our example builders again:

public abstract class PersonBuilder<T extends PersonBuilder> {
  
  private int height;
  private int weight;

  @SuppressWarnings("unchecked")
  public T height(int height) {
    this.height = height;
    return (T) this;
  }

  @SuppressWarnings("unchecked")
  public T weight(int weight) {
    this.weight = weight;
    return (T) this;
  }

  public Person buildPerson() {
    Person person = new Person();
    person.setHeight(height);
    person.setWeight(weight);
    return person;
  }
}

public class WrestlerBuilder extends PersonBuilder<WrestlerBuilder> {

  private String stageName;

  public WrestlerBuilder stageName(String stageName) {
    this.stageName = stageName;
    return this;
  }

  @Override
  public Person buildPerson() {
    Wrestler wrestler = new Wrestler(super.buildPerson());
    wrestler.setStageName(stageName);
    return employee;
  }
}

Now the height and weight methods on WrestlerBuilder objects have return type WrestlerBuilder, so we can chain methods from both classes:

new WrestlerBuilder()
  .height(42)
  .weight(50) 
  .stageName("Mucha Lucha")
  .buildPerson()

We just have to ensure that the subclasses use their own class as a parameter.