So last year I posted about using Joshua Bloch’s Builder pattern to create objects from a hierarchy of subclasses.
I realized later that while I did have something like this completely working at one point, I had it working in C#, which has deceptively different generic semantics. (It also has named parameters, which makes the Builder pattern a fair bit less necessary.) As Zdenek Henek demonstrated (several weeks ago, sorry Zdenek!), in Java, the original version I posted doesn’t allow you to call the arguments in any order.
This one does:
class Shape { private final double opacity; public double getOpacity () { return opacity; } public static abstract class ShapeBuilder<S extends Shape, B extends ShapeBuilder<S, B>> { private double opacity; @SuppressWarnings( "unchecked" ) public B opacity ( double opacity ) { this.opacity = opacity; return (B) this; } public abstract S build (); } private static class DefaultShapeBuilder extends ShapeBuilder<Shape, DefaultShapeBuilder> { @Override public Shape build () { return new Shape( this ); } } public static ShapeBuilder<?, ?> builder () { return new DefaultShapeBuilder(); } protected Shape ( ShapeBuilder<?, ?> builder ) { this.opacity = builder.opacity; } } class Rectangle extends Shape { private final double height; private final double width; public double getHeight () { return height; } public double getWidth () { return width; } public static abstract class RectangleBuilder<S extends Rectangle, B extends RectangleBuilder<S, B>> extends ShapeBuilder<S, B> { private double height; private double width; @SuppressWarnings( "unchecked" ) public B height ( double height ) { this.height = height; return (B) this; } @SuppressWarnings( "unchecked" ) public B width ( double width ) { this.width = width; return (B) this; } } public static RectangleBuilder<?, ?> builder () { return new DefaultRectangleBuilder(); } protected Rectangle ( RectangleBuilder<?, ?> builder ) { super( builder ); this.height = builder.height; this.width = builder.width; } private static class DefaultRectangleBuilder extends RectangleBuilder<Rectangle, DefaultRectangleBuilder> { @Override public Rectangle build () { return new Rectangle( this ); } } } class RotatedRectangle extends Rectangle { private final double theta; public double getTheta () { return theta; } public static abstract class RotatedRectangleBuilder<S extends RotatedRectangle, B extends RotatedRectangleBuilder<S, B>> extends Rectangle.RectangleBuilder<S, B> { private double theta; @SuppressWarnings( "Unchecked" ) public B theta ( double theta ) { this.theta = theta; return (B) this; } } public static RotatedRectangleBuilder<?, ?> builder () { return new DefaultRotatedRectangleBuilder(); } protected RotatedRectangle ( RotatedRectangleBuilder<?, ?> builder ) { super( builder ); this.theta = builder.theta; } private static class DefaultRotatedRectangleBuilder extends RotatedRectangleBuilder<RotatedRectangle, DefaultRotatedRectangleBuilder> { @Override public RotatedRectangle build () { return new RotatedRectangle( this ); } } } class BuilderTest { public static void main ( String[] args ) { RotatedRectangle rotatedRectangle = RotatedRectangle.builder() .theta( Math.PI / 2 ) .width( 640 ) .height( 400 ) .height( 400 ) .opacity( 0.5d ) .width( 111 ) .opacity( 0.5d ) .width( 222 ) .height( 400 ) .width( 640 ) .width( 640 ) .build(); System.out.println( rotatedRectangle.getTheta() ); System.out.println( rotatedRectangle.getWidth() ); System.out.println( rotatedRectangle.getHeight() ); System.out.println( rotatedRectangle.getOpacity() ); } }
Note though it requires some unchecked casts, and depends on the convention that each Builder’s generics are self-referential, which could present some risk if it’s extended further. Test early, test often.