I am reading Effective Java Edition 3. in chapter 2 page 14, the author talks about the builder pattern and presents this code:
public abstract class Pizza {
public enum Topping { HAM, MUSHROOM, ONION, PEPPER, SAUSAGE }
final Set<Topping> toppings;
abstract static class Builder<T extends Builder<T>> {
EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class);
public T addTopping(Topping topping) {
toppings.add(Objects.requireNonNull(topping));
return self();
}
abstract Pizza build();
// Subclasses must override this method to return "this"
protected abstract T self();
}
Pizza(Builder<?> builder) {
toppings = builder.toppings.clone(); // See Item 50
}
}
an implementation of above abstract class:
public class NyPizza extends Pizza {
public enum Size { SMALL, MEDIUM, LARGE }
private final Size size;
public static class Builder extends Pizza.Builder<Builder> {
private final Size size;
public Builder(Size size) {
this.size = Objects.requireNonNull(size);
}
@Override public NyPizza build() {
return new NyPizza(this);
}
@Override protected Builder self() { return this; }
}
private NyPizza(Builder builder) {
super(builder);
size = builder.size;
}
}
and we can use code like this:
NyPizza pizza = new NyPizza.Builder(SMALL)
.addTopping(SAUSAGE).addTopping(ONION).build();
quote from the book:
Note that
Pizza.Builder
is a generic type with recursive type parameter. this, along with the abstract self method, allows method chaining to work properly in subclasses, without the need for casts.
now my question is what power/value <T extends Builder<T>>
added to Pizza
class and how it is different with <T extends Builder>
? if you are going to explain <T extends Builder<T>>
to a five years old kid in simple English how would you explain it?
and I can't figure out the purpose of abstract self method in the super class?
I add this part because of the comment section
Imagine I have changed the above codes like this(it is not going to be the best example just for illustration purposes):
I changed the NyPizza.Builder
to be generic:
public class NyPizza extends Pizza {
public enum Size { SMALL, MEDIUM, LARGE }
private final Size size;
public static class Builder<T> extends Pizza.Builder<Builder<T>> {
private final Size size;
public Builder(Size size) {
this.size = Objects.requireNonNull(size);
}
@Override public NyPizza build() {
return new NyPizza(this);
}
@Override protected Builder self() { return this; }
}
private NyPizza(Builder builder) {
super(builder);
size = builder.size;
}
}
and the Pizza
class like this:
public abstract class Pizza {
public enum Topping { HAM, MUSHROOM, ONION, PEPPER, SAUSAGE }
final Set<Topping> toppings;
abstract static class Builder<T extends Builder> {
T obj;
EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class);
public T addTopping(Topping topping) {
toppings.add(Objects.requireNonNull(topping));
return self();
}
public T builder(){
return obj;
}
abstract Pizza build();
// Subclasses must override this method to return "this"
protected abstract T self();
}
Pizza(Builder<?> builder) {
toppings = builder.toppings.clone(); // See Item 50
}
}
and used the above classes like this:
NyPizza.Builder<String> test = new NyPizza.Builder<String>(SMALL).builder();
now because I didn't define class Builder<T extends Builder>
in this form: class Builder<T extends Builder<T>>
the builder
method in the Pizza
class should not be able to detect the type of the T is NyPizza.Builder<String>
but it can. how is it possible? how change it to need casting?