user5182503
user5182503

Reputation:

Builder patterns and generic type that extends itself

I am trying to implement Builder pattern with inheritance and found the following solution .

This is B class:

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
    }
}

Now I want to make class C also extendable. This is the way I do it:

public class C extends B {
    ...
    public static class Builder<T extends Builder<T>> extends B.Builder<T> {
        ....
    }
}

So, in C we have builder that can be extended in, for example, D. My question is what is the right way to create C.Builder instance. I mean, what should I set in its generics:

C.Builder<WHAT HERE> builder = new C.Builder<>();

Upvotes: 4

Views: 1670

Answers (1)

john16384
john16384

Reputation: 8074

Since the argument T for C.Builder must be something that extends C.Builder you must create another class that extends C.Builder, as you cannot use C.Builder itself as it is parameterized.

A solution therefore for inherited builders is to create two builders, one which is generic, extensible, protected and abstract, and another one that is public, final and not generic used for the actual building.

Here are sample classes that show this:

public class Person {
  private final String name;

  public Person(String name) {
    this.name = name;
  }

  public String getName() {
    return name;
  }

  protected static abstract class ExtensiblePersonBuilder<S, T extends Person> {
    protected String name;

    public S name(String name) {
      this.name = name;
      return self();
    }

    public abstract S self();
    public abstract T build();
  }

  public static final class PersonBuilder extends ExtensiblePersonBuilder<PersonBuilder, Person> {
    @Override
    public PersonBuilder self() {
      return this;
    }

    @Override
    public Person build() {
      return new Person(name);
    }
  }
}

And a subclass, with builder:

  public class Employee extends Person {
    private int number;

    public Employee(String name, int number) {
      super(name);

      this.number = number;
    }

    protected static abstract class ExtensibleEmployeeBuilder<S, T extends Employee> extends ExtensiblePersonBuilder<S, T> {
      protected int number;

      public S number(int number) {
        this.number = number;
        return self();
      }
    }

    public static final class EmployeeBuilder extends ExtensibleEmployeeBuilder<EmployeeBuilder, Employee> {

      @Override
      public EmployeeBuilder self() {
        return this;
      }

      @Override
      public Employee build() {
        return new Employee(name, number);
      }
    }
  }

And a sub sub class:

public class Manager extends Employee {
  private String teamName;

  public Manager(String name, int number, String teamName) {
    super(name, number);

    this.teamName = teamName;
  }

  protected static abstract class ExtensibleManagerBuilder<S, T extends Manager> extends ExtensibleEmployeeBuilder<S, T> {
    protected String teamName;

    public S teamName(String teamName) {
      this.teamName = teamName;
      return self();
    }
  }

  public static final class ManagerBuilder extends ExtensibleManagerBuilder<ManagerBuilder, Manager> {
    @Override
    public ManagerBuilder self() {
      return this;
    }

    @Override
    public Manager build() {
      return new Manager(name, number, teamName);
    }
  }
}

You can use these builders like this:

Person p = new PersonBuilder().name("John").build();

or:

Manager m = new ManagerBuilder().name("Jeff").teamName("X").number(2).build();

Upvotes: 4

Related Questions