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);
}
}
I wrote the same thing in Java, then decided that it didn’t readably scale up to 10 parameters or so, which is the case that Builder is actually a useful pattern for. X[FALSE, FALSE, FALSE, FALSE, FALSE..] is difficult to call readable.
I don’t have a better answer though.
[...] Type-safe Builder Pattern in Java « Michid’s Weblog – 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. [...]
Nice!
@Ricky, I guess even with as many as ten parameters it’s plausible to call the entire builder in one expression, in which case the Initializer’s type is never named in the calling code.
Matt,
True, but you see it in the implementation and in the IDE’s prompting while you’re writing the use. And if you work under a coding standard that prefers naming subexpressions to writing long lines (even with wrapping) the lack of type inference in Java makes the pain return.
Hi! Actually, in Java you can use a much simpler approach to get type-safe Builder (although Rafael’s approach looks nice it’s not really required in such case). You just declare a Builder’s constructor, that takes all required parameters. Then you set all optionals via chained Builder method calls.
If there are more required params, overloaded Builder constructor may help. In most complex cases (10 or more req. params), you can put static factory methods to Builder.
So phantom types are cool, but in this case there is better (and more KISSy) solution IMHO.