Why Matthias Ernst’s “Chaining: A Modest Language Proposal” for Java would be nice to have

February 14, 2009

OK, shouting at my MP3 player hasn’t been doing me any good; it’s just helping other drivers recognize how passionate I am. ;-> So I’ll try writing about it instead:

After listening to recent Java Posse podcast episodes, I’ve been working my way back to earlier episodes, and I keep tripping over discussions (as in Episide #151) of Matthias Ernst’s “Chaining: A Modest Language Proposal” — which would make the compiler enable a method chaining / “fluid interface” convention on methods returning the void type.

Like the Java Posse members, I’m also somewhat bothered by the idea of reinterpreting void return types, but I think it will be helpful to consider this example of code that would be fixed by his proposal:

Consider these two classes:
public class Base {
  public Base setBaseStuff() { /* ... */ return this; }
public class Sub extends Base {
  public Sub setSubStuff() { /* ... */ return this; }

This code works:
  new Sub().setSubStuff().setBaseStuff();
This code doesn’t compile:
  new Sub().setBaseStuff().setSubStuff();

The problem is that when you’re using method chaining on classes that extend other classes and which add methods…  You MUST call subclass methods before calling superclass methods.  Otherwise it won’t compile.

With the “Chained Invocations” proposal, if the methods returned void, both method calling orders would compile and work fine.

This happens because the compiler knows more about the type of the callee than the method declaration: When the compiler assumes that void methods return ‘this’, it knows the specific subclass type of ‘this’ — while the method declarations don’t.

In essence…
  new Sub().setBaseStuff().setSubStuff();
  Sub s = new Sub();

This may not seem like much. But what happens when you have a significant number of methods, and possibly several inheritance levels?

Consider these classes:

  public class A {
    public A a1() { /* ... */ return this; }
    public A a2() { /* ... */ return this; }
    public A a3() { /* ... */ return this; }
  public class B extends A {
    public B b1() { /* ... */ return this; }
    public B b2() { /* ... */ return this; }
    public B b3() { /* ... */ return this; }
  public class C extends B {
    public C c1() { /* ... */ return this; }
    public C c2() { /* ... */ return this; }
    public C c3() { /* ... */ return this; }

And this expression:
  new C().c1().c2().c3().b1().b2().b3().a1().a2().a3();

Like this:
  new C().c1().c2().a2().c3().b1().b2().b3().a1().a3();

So are we on the same page now, as to why this change might be nice to have?