4

Is there a way to avoid the unchecked class cast in this hierarchical Builder pattern?

public abstract class BaseBuilder <T, B extends BaseBuilder<T,B>> {

  public B setB1(String b1) {
    this.b1 = b1;
    return (B) this; // can I make this unchecked cast go away?
  }

  abstract public T build();

  String b1;
}

and no, the answer is not:

return B.class.cast(this);

and yes, I know I could use @SuppressWarnings

Eric
  • 322
  • 3
  • 9
  • 4
    Unfortunately, Java doesn't allow for enforcing B to be the "current" class, and the unchecked warning is there to remind you that. `class MBB1`, `class MBB2`, what do you expect `MBB1.setB1()` to do? – ignis Dec 12 '12 at 18:05
  • 1
    related: http://stackoverflow.com/questions/7354740/is-there-a-way-to-refer-to-the-current-type-with-a-type-variable – Paul Bellora Dec 12 '12 at 22:18

3 Answers3

5

As said before, this can't be done, because it is not safe. B extends BaseBuilder<T,B>, but BaseBuilder<T,B> (type of this) does not extend B. Recursive bounds are almost NEVER useful in Java, and do not give you the self-type. You should get rid of it.

You can add an abstract method such that implementing classes must give an instance of B:

public abstract class BaseBuilder <T, B> {

  abstract public B getB();

  public B setB1(String b1) {
    this.b1 = b1;
    return getB();
  }

  abstract public T build();

  String b1;
}
newacct
  • 119,665
  • 29
  • 163
  • 224
  • I hadn't thought of the shenanigans like: `public class EvilBuilder extends BaseBuilder {...` – Eric Dec 13 '12 at 00:09
2

Yes; return BaseBuilder<T, B> and force subclasses to override setB1 to return themselves.

Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
  • 1
    @Saintali: they don't have to return themselves. They just have to return an instance of `B`. So all you need is to have a return type of `B`. – newacct Dec 12 '12 at 22:39
1

besides of what louis said the following is a somewhat nice design:

public abstract class BaseBuilder ... {
    ...
    public B setB1(String b1) {
        this.b1 = b1;
        return self();
    }

    abstract protected B self();
    ...
}

public class SomeBuilder extends BaseBuilder ... {
    @override
    protected SomeBuilder self() {
        return this;
    }
}
Julien May
  • 2,011
  • 15
  • 18