Studying Builder Pattern with Inheritance

Just to pay some tech debts.

Builder Pattern alone is easy to understand, but not so much if inheritance is concerned.

(Part I) understand the problem

First, we need to understand how inner class works.

Given A <— B (B is subclass of A), and A.Builder is inner class of A, and B.Builder is inner class of B. If we have A.Builder <— B.Builder as well, what is the consequence of following calls?

  1. new B.Builder(): A and B are not created, but A.Builder() is called and then B.Builder() is called. new B.Builder().getClass()returns B$Builder;
  2. new B.Builder().Bmethod(): call B’s Bmethod (who returns B$Builder); getClass() returns B$Builder;
  3. new B.Builder().Bmethod().Amethod(): call A’s Amethod (who returns A$Builder); but getClass() still returns B$Builder();
  4. new B.Builder().Bmethod().Amethod().build(): call B’s build() (who returns new B(B$Builder)) because Amethod return “this”, by no means compiler knows who is “this”; Once B(B$Builder) is called, first B’s super(B$Builder) is called, and then B’s rest is called. So finally, B is created and returned. However, this only happens in runtime. In compile time, the build() that is involved is A’s build() . So casting has to happen so that compiler can pass.

Compile time follows the definition/ signature strictly, but in runtime, jvm finds the nearest method to call. Method getclass() only returns the runtime class, and down cast (from parent to child) is only possible if in runtime parent type is actually a child object. Compiler does not know what is “this” it is referring, so runtime cast must be done.

Therefore, as Eamonn McManus mentioned in the post,

new B.Builder().Bmethod().Amethod().build() will be compiled, but the sequence requirement is not a pleasant requirement, and a down cast must be there in order to do sth. like

B b = (B) new B.Builder().Bmethod().Amethod().build();

(Part II) explore to solve the problem

In summary, the problem happens because in parent class A$Builder we have a method Amethod who returns this, but in compile time, compiler correctly thinks “this” is A$Builder, but what we hope is that “this” is B$Builder. So we need a trick to cheat compiler, here we go,

  1. the getThis() trick. What the trick does is that instead of directly return “this” in parents, it calls a “getThis()” method to return a generic type “T”, such as protected abstract T getThis(); in parent class A.
  2. However, “T”cannot be unbounded, otherwise T.build() or T.Amethod() cannot be compiled. Thus “T” must extends Builder. In A, we try

    public abstract static class Builder<T extends Builder>

  3. However, that does not solve the sequence problem. The “T” returned is any Builder, not the B$Builder we are trying to get. So we need to pass in the B$Builder to A$Builder‘s constructor. That is, in B we have

    public static class Builder extends A.Builder<Builder>

  4. However, again, it is not restrict enough. If accidentally, in B, we do

    public static class Builder extends A.Builder<A.Builder>

    It will still compile, but the logic is wrong since we are not getting B$Builder. So we must remember what we want for “T” returned in A is exactly B, not anything else, so in A we restrict ourselves, as per self-referential type

    public abstract static class Builder<T extends Builder<T>> 

(Part III)

So for two level, A and B, we can summarize as

public class A {
    public static abstract class Builder<T extends Builder<T>> {
        protected abstract T getThis();
        public T Amethod() {
            //implementation
            return getThis();
        }
        public A build() {
            return new A(this);
        }
    }
    protected A(Builder builder) {
        //field assignment
    }
}

public class B extends A {
    public static class Builder extends A.Builder<Builder> {
        @Override
        public Builder getThis() {
            return this;
        }
        public Builder Bmethod() {
            //implementation
            return this;
        }
        public B build() {
            return new B(this);
        }
    }
    private B(Builder builder) {
        super(builder);
        //B field assignment
    }
}

//it is ok to do
new B.Builder().A/Bmethod().build()

However, it is not possible to do new A.Builder().build(); because of missing argument.

What if this time, I need A<–B<–C, and B, C will be able to create instance. I need more work to do.

new C.Builder().Bmethod().Amethod().build() will not compile, because again Bmethod() returns “this” referring to B$Builder, and B$Builder.build() returns B, instead of C. So, apply getThis() trick again.

  1. Working on B, not touching A. Since Bmethod() returns B$Builder, we modify it to return “T”, and thus Bmethod()‘s “return this” changes to “return getThis()“. In addition, override method getThis() changes to protected abstract T getThis();, and consecutively, we need to declare B$Builder to be abstract.  public static abstract class Builder<T extends Builder<T>> extends A.Builder<T>;
  2. This will violate the usage of new B.Builder(), since it is abstract. Let’s put this aside with a mark first.
    For C, C needs to have a Builder class extends B.Builder and implements getThis() by returning “this”.
    The C$Builder is declared as public static class Builder extends B.Builder<Builder>

So in Summary,

public class B extends A {
    public static abstract class Builder<T extends Builder<T>>
        extends A.Builder<T> {
        @Override
        public abstract T getThis();public Builder Bmethod() {
            //implementation
            return this;
        }
        public B build() {
            return new B(this);
        }
    }
    private B(Builder builder) {
        super(builder);
        //B field assignment
    }
}
public class C extends B {
    public static class Builder extends B.Builder<Builder> {
        @Override
        public Builder getThis() {
            return this;
        }

public Builder Cmethod() {
            //implementation
            return this;
        }
        public C build() {
            return new C(this);
        }
    }
    private C(Builder builder) {
        super(builder);
        //C field assignment
    }
}
// it is ok to do
new C.Builder().A/B/Cmethod().build();
// But this will fail, since B$Builder is abstract.
new B.Builder()....

(Part IV)

The problem with the recursive generic is the parameter must be a parameterized subtype i.e. if you have:

class A$Builder<T extends A$Builder<T>> {} 
class B$Builder<T extends B$Builder<T>> extends A$Builder<T> {} 
class C$Builder extends B$Builder<C$Builder> {}

You can do new C$Builder(); but not new B$Builder<B$Builder>();

So you do need some kind of an “interim concrete” class.

I got above comments from this.

So the most promising solution is the 1st reference. Using “init” inner class.

  1. Go to Class B, update all previous abstract Builder Class to Init Class. So A needs to define Init as well.
  2. In A, A’s constructor will take Init object instead of previous builder object. If A is not instantiate-able, A$Builder is not required.
  3. In B, create a static non-abstract Builder by extends B$Init<B$Builder>. Besides, B$Builder needs to implement getThis() by returning “this”. And Update B’s constructor by pointing to init object, rather than previous builder object.
  4. In C, it does not need any more concrete class, simply do public static class Builder extends B.Init<Builder>

(Part V)

But use Init, seriously?? It generally confuses me once I look back.
I like to keep Builder Pattern, and I don’t want to remember an Init Class.
I follow the Builder2 Pattern in the 1st reference, which is called by getBuilder() method.

So revert to Part III’s final result first.

  1. Go to B, since B is the one that cannot be instantiated.  Create a static method getBuilder() which returns an InterimBuilder. NOTE the return type must be Builder<?>. Because if without <?>, the type information will be lost after calling new InterimBuilder(); For the reason please refer to my answer in StackOverflow.
  2. In B, define static class InterimBuilder extends Builder and implement its own getThis()
  3. As a caller, it is possible to do B.getBuilder().Bmethod()
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: