Generic variable to the current type

I just noticed that I’ve been using the Builder pattern with method chaining a lot. Looks like this:

FooBuilder builder = new FooBuilder();
Foo foo = builder.add(...).divide(...).multiple(...).builder();

The issue that arises is that I want to store a bunch of methods in a base class that various builders can use. In order to do this correctly, I have to really hack up my classes with some strange generics and unchecked casts like this:

public class BaseBuilder<T extends BaseBuilder> {
  public T add(...) {
    ...
    return (T) this;
  }
  public T divide(...) {
    ...
    return (T) this;
  }
  public T multiply(...) {
    ...
    return (T) this;
  }
}

public class FooBuilder extends BaseBuilder<FooBuilder> {
  public Foo build() {
    ...
  }
}

This is really annoying. I think every class should have a generic type variable that references the current type and let the compiler figure it out. Just name the variable ME or something. It would make the code look like this instead:

public class BaseBuilder{
  public ME add(...) {
    ...
    return this;
  }
  public ME divide(...) {
    ...
    return this;
  }
  public ME multiply(...) {
    ...
    return this;
  }
}

public class FooBuilder extends BaseBuilder{
  public Foo build() {
    ...
  }
}

Now that’s much nicer looking.

6 thoughts on “Generic variable to the current type

  1. Scala lets you improve this a bit with a self type annotation. I don’t find the type parameter that ugly (after all, you need to get the actual type in there somehow), but the unchecked casts are nasty. Here’s the pattern in Scala:

    class BaseBuilder[T
    def add(): T = {
    //…
    this
    }
    def divide(): T = {
    //…
    this
    }
    def multiply(): T = {
    //…
    this
    }
    }

    class FooBuilder extends BaseBuilder[FooBuilder] {
    }

    The “this: T =>” in the first line is the key. It says to treat “this” as having a static type of T rather than BaseBuilder. If anybody tries to subclass BaseBuilder without satisfying that requirement, it’s a compile error. (You can get the same effect by just doing “class BaseBuilder[T] { this: T => … }”, which just leaves off the bounds on T, but the error messages are uglier.) Anyway it basically allows T to act just like your ME type, and the compiler enforces it.

    Incidentally, I’d make a few other changes if I were doing it in Scala: I’d make BaseBuilder a trait and clean up the syntax of the methods a bit. But basically that’s the idea…

    Like

  2. Stupid WordPress ate my pre tags and my less-than sign… Trying again, the first line should be:

    class BaseBuilder[T <: BaseBuilder[T]] { this: T =>

    Will that work?

    Like

  3. Yeah, that will work also. Either is fine. The compiler doesn’t care that the generic variable isn’t filled out in the definition. At least Java doesn’t care since it erases everything anyways and allows raw instances for generically typed classes.

    The idea isn’t that it can’t be done in any language, just that it sucks to have to hack the language like that. With co-variant return types, we should be capable of creating these types of methods without any generic information. Actually, you could simplify it like this:

    class BaseBuilder {
      public this add() {
      }
    }
    
    class FooBuilder extends BaseBuilder {
    }
    

    That’s the cleanest way.

    Like

  4. It’s tempting to introduce a special keyword like “ME” or “this” to mean something like “the concrete type of the eventual subclass.” But I think you’ll run into problems if you actually try to implement a type-checker for this language. The problem comes when checking a method call like “b.add().foo()” when the known type of b is just BaseBuilder (i.e., not a concrete subclass). The problem is: what does add return? You know it must be a BaseBuilder, but the whole point of the “this” return type is that you really need to know, for example, that it’s a FooBuilder: knowing that it’s BaseBuilder isn’t enough if foo() is only defined on FooBuilder.

    This is the problem that the “extra” generic solves. If instead of BaseBuilder, you know that b is a BaseBuilder[FooBuilder], and instead of “ME” or “this” add returns a T (== FooBuilder, in this case), then the typechecker has the information that it needs to typecheck a call like “b.add().foo()”. So in some sense the generic isn’t extra: it serves to make the eventual concrete subclass known to the typechecker.

    Like

  5. See, they are identical in my case. The ‘this’ keyword is a compiler indicator to use the type of the variable. If b is a BaseBuilder, the method’s return type will be BaseBuilder. If b is a FooBuilder, it will be a FooBuilder. This is the exact same way the generic works.

    BaseBuilder[FooBuilder] b;
    b.add().build();
    
    FooBuilder b;
    b.add().build();
    

    These are the same. The type of B can _only_ be a FooBuilder or a sub-class for both. If you further cast out the known type, the compiler should fail in both cases:

    BaseBuilder[FooBuilder] b;
    BaseBuilder b_raw = b
    b.add().build(); // FAIL
    
    FooBuilder b;
    BaseBuilder b_raw = b;
    b.add().build(); // FAIL
    

    Using a keyword (I like ‘this’) does the same thing (from the compilers perspective) as adding the generic information. It just provides a simpler mechanism with less cruft.

    Like

Leave a comment