Jevgeni has posted 19 posts at DZone. You can read more from them at their website. View Full User Profile

Concise Embedded DSL Closures in Java

05.22.2008
| 5058 views |
  • submit to reddit

This trick was pointed to me (without the ThreadLocal part) by Rein Raudjärv, who saw it used in jMock.

The problem with Java “closures” aka anonymous inner classes being a tad too ugly is a well known one, but this semi-solution seems to be quite unused. Although you can apply the same principle for many situations it’ll work best for embedded DSLs.

Let’s start by defining an EDSL intefrace:

public interface DSL {
DSL doSomething();
DSL doSomethingElse();
DSLPreClosure closure();
}

As you can see we separated the actual closure application in a separate interface:


public interface DSLPreClosure {
DSL apply(DSLClosure closure);
}

The reason for that is that we want to use closures without defining a method in the anonymous inner class:


public class Test {
public static void main(String[] args) {
new DSLImpl()
.doSomething()
.closure().apply(new DSLClosure() {{
dsl
.doSomething()
.doSomethingElse();
}})
.doSomethingElse();
}
}

What happens is that we abuse a little used Java feature — initializers. If you put a block in the body of a class definition it will be executed right after the constructor. In our case we construct a new subclass of DSLClosure and immediately add an initializer:


new DSLClosure() {
//Initializer starts here
{
//Closure body goes here
}
//Initializer ends here
};

Of course while this allows to execute a block of code with fewer symbols to write, we still need to somehow pass parameters. Note, that since the initializer executes inside the constructor call, it will be executed before the actual apply() method is called. Therefore we make use of the ThreadLocal class to pass the parameters inside the call:


public class DSLImpl implements DSLWithClosure {
static final ThreadLocal curDSL =
new ThreadLocal();
public DSL doSomething() {
//Do something…
return this;
}
public DSL doSomethingElse() {
//Do something else…
return this;
}
public DSLWithClosure closure() {
curDSL.set(this);
return this;
}
public DSL apply(DSLClosure closure) {
curDSL.set(null);
return this;
}
}

Finally the DSLClosure exposes the ThreadLocal parameter as a protected field:


public class DSLClosure {
protected DSL dsl =
(DSL) DSLImpl.curDSL.get();
}

Of course, for the sake of correctness we should also allow nesting closure calls on the same thread, so we should use a stack inside ThreadLocal, but this is left as an exercise to the reader :).

Jevgeni Kabanov is the Tech Lead of ZeroTurnaround, makers of JavaRebel. You can find more of his writing at the http://dow.ngra.de blog.

References
Published at DZone with permission of its author, Jevgeni Kabanov. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Ricky Clarkson replied on Thu, 2008/05/22 - 6:09am

While technically that uses a closure (in the Scheme definition of closure) it's not a block of code you have a reference to (everyone else's definition of closure).

DSL d=new DSL() {{ System.out.println("Once"); }};

does the same thing as:

DSL d=new DSL(); System.out.println("Once");

There's nothing you can invoke on d to make Once appear twice in the first example.

Jevgeni Kabanov replied on Thu, 2008/05/22 - 6:52am in response to: Ricky Clarkson

@Ricky

 If you want that just define it as an inner class:

class  MyClosure extends DSLClosure {{
dsl.doSomething();
}}
...
dsl.closure().apply(new MyClosure()));

Ricky Clarkson replied on Thu, 2008/05/22 - 10:11am

No, you've misunderstood me.  The usual meaning of closure is a block of code you have a reference to (more restrictive definitions say function or method instead of block) that can use values from the enclosing scope.  You can run that block 0, 1 or many times through that reference.  For example (using [] for generics):

final int x=5;

someList.foreach(new SideEffect[String](){ public void invoke(String s) { if (s.length()>x) System.out.println(s); } });

That just isn't the same as an initialiser at all.  The initialiser happens exactly once.  It's not a block of code you have a reference to.

Jevgeni Kabanov replied on Thu, 2008/05/22 - 2:27pm in response to: Ricky Clarkson

@Ricky

In fact you can do exactly that with the inner class, if you pass it as MyClosure.class and let the method construct the instance. You can even make a framework around it and pass arguments via constructor. Of course you would lose type safety and generally it won't look very well. The case I've shown is useful for inserting control flow and reusability in a chained-method EDSL, which was my main interest, therefore the article name :)

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.