Type-safe Builder Pattern in Java

13 08 2008

In this post I deviate a bit from the topic of my recent posts about Meta-Programming with Scala. I will have more to say about the latter topic in upcoming posts however.

Recently I read this rather fascinating post about a Type-safe Builder Pattern in Scala. When Heinz Kabutz mentioned the builder pattern in his latest issues of the The Java Specialists’ Newsletter I decided to try to come up with a type safe version for Java.

What I finally came up with is not strictly a builder but something I rather call an initializer. The initializer contains the initial state required by the target object. The state is accumulated within the initializer. Only when the state is complete can it be passed to the targets object’s constructor. Java’s type system prevents passing a initializer with an incomplete state to the target class’s constructor.

public class Foo {
  private final int a;
  private final int b;

  public Foo(Initializer<TRUE, TRUE> initializer) {
    this(initializer.a, initializer.b);
  }

  private Foo(int a, int b) {
    super();
    this.a = a;
    this.b = b;
  }

  public String toString() {
    return "a = " + a + ", b = " + b;
  }

  public static class Initializer<HA, HB> {
    private int a;
    private int b;

    private Initializer() {
      super();
    }

    private Initializer(int a, int b) {
      super();
      this.a = a;
      this.b = b;
    }

    public static Initializer<FALSE, FALSE>create() {
      return new Initializer<FALSE, FALSE>();
    }

    public Initializer<TRUE, HB> setA(int a) {
      this.a = a;
      return new Initializer<TRUE, HB>(a, this.b);
    }

    public Initializer<HA, TRUE> setB(int b) {
      this.b = b;
      return new Initializer<HA, TRUE>(this.a, b);
    }

    static abstract class TRUE {}
    static abstract class FALSE {}
  }

}

The basic technique is the same as for the Type-safe Builder Pattern in Scala: the phantom types TRUE and FALSE are used to keep track of the state. Only a complete state will result in a Initialiter instance which subsequently can be passed to Foo‘s constructor.

Here is how this is used:

public class Main {
  public static void main(String[] args) {
    Initializer<?, ?> initializer = Initializer.create();

    // Foo.create(initializer);           // won't compile
    // Foo.create(initializer.setB(1));   // won't compile
    // Foo.create(initializer.setA(1));   // won't compile

    Foo foo = new Foo(initializer.setA(1).setB(2));
    System.out.println(foo);
  }
}