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?
new B.Builder(): A and B are not created, but
A.Builder()is called and then
B.Builder()is called. new
new B.Builder().Bmethod(): call B’s Bmethod (who returns
new B.Builder().Bmethod().Amethod(): call A’s Amethod (who returns
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,