Dennis has posted 2 posts at DZone. View Full User Profile

Yet Another Reason Why Java Needs Closures

06.17.2008
| 4743 views |
  • submit to reddit

Ever try to unit test code with static method calls? That's right. You know how this feels.

 

class  Untestable{
public void doSomething(){
...
if ( Util.openSocketAndThenGoToFileSystem() )
...
}
}
Unit testing doSomething is often impossible because unit tests simply don't do things like talk to networks or file systems. It's even more "fun" when someone puts code like this in a constructor - that way you can't even instantiate an instance of Untestable outside the production environment.

This is particularly painful in Java (as it stands today) for two reasons. First, there are no open classes. Sure, there is conditional compilation or cglib but at the end of the day, it's never going to be as easy as it is in JavaScript, Python, etc.

Second, it calls a static method. If you haven't read Steve Yegge's post on this matter, verbs are second class citizens in Java. Because each verb must be escorted by a noun at all times, methods cannot be stubbed out or mocked. This limitation has profound side effects on the maintainability (and profitability) of every large Java code base.

Enter Closures ...


We can refactor the Untestable class to a Testable class.
class Testable{

private final { => boolean } collaborator;

Testable({ => boolean } collaborator){
this.collaborator = collaborator;
}

public void doSomething(){
...
if ( collaborator.invoke() )
...
}
}
By declaring a closure instance variable we can use Dependency Injection to exercise the functionality of Testable without doing something expensive. This is a lot easier than retrofitting an interface or inner class. Our test methods now look like this:

public void testDoSomething(){
Testable testable = new Testable({ => true });
testable.doSomething();
assertTrue(true, testable.someSideEffect());
}



Neal Gafter tells me the chances of closures making it into the next version of Java is getting smaller and smaller. I'm keeping my fingers crossed, time will tell. Keep up the good work Neal.

From http://notdennisbyrne.blogspot.com/

Published at DZone with permission of its author, Dennis Byrne.

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

Tags:

Comments

Jean-Baptiste B... replied on Tue, 2008/06/17 - 3:10am

Denis,

As you said, there is no limitation as per the java bytecode (cf. cglib & co)... so anybody is free to use another language and get it run on a JVM ;-)

I don't think "closure" is a top priority for language improvements. Moreover, if you compare with things such as :

  1. Full genericity (Runtime aware) = required by toolkit to do better reflection jobs (think automagic form display, think better persistence, etc)
  2. Finding method parameter names using reflect API (removed from JDK6beta2 plans) = required by some languages and to do better marshaling (think SOAP, REST, etc)
  3. property (isn't this ridiculous to decorate empty setters/getters most of the times)
  4. maybe even a javabeans 2.0 (including validator, resource management, sequencing, etc)
Should the closure be implemented at the end ? Maybe, but, only once every subtilities are fixed.

 Rgs,

JB

Greg Allen replied on Tue, 2008/06/17 - 3:47am

Actually, this is why Java should not have closures! Look at the signature for that constructor - urgh. Awful readability.

Consider what it becomes when you need two boolean-returning closures as constructor params - no type safety.

Instead a simple interface will do the trick just fine. It can even be an inner-class-like one, like Map.Entry. The method(s) can then have a meaningful name.

Also, if you just write the interface name and use its method before you've even defined it, you can easily let Eclipse create the interface, and then the method, for you with Ctrl-1, Quick Fix. So the typing required is about the same as a closure.

Jess Holle replied on Tue, 2008/06/17 - 6:52am

Many purported limitations of unit testing on Java are just limitations of the unit testing tools being used.

For instance, if you're using EasyMock and blaming Java or Java coding patterns, then stop using EasyMock.

Instead, look at jmockit, where you can replace methods on classes for the duration of your unit test and restore them.

I'm not a jmockit fan boy -- nothing is perfect -- but I'm tired of "you can't unit test pattern ___ in Java, so you need to rework how you code to do it like ____" when the real problem is underpowered unit testing tools.  You shouldn't have to contort the language or your coding patterns just to support unit testing.  [If you have other architectural reasons to refactor that's well and good, of course.]

Brian Sayatovic replied on Tue, 2008/06/17 - 7:47am in response to: Greg Allen

Mod up!

I second this! What does the closure have to offer that you couldn't accomplish with an interface?

class Testable{  
  
    interface Collaborator {
      public boolean invoke();
    }
    private final Collaborator collaborator;  
  
    Testable(Collaborator collaborator){  
       this.collaborator = collaborator;  
    }  
  
    public void doSomething(){  
        ...  
        if ( collaborator.invoke() )  
        ...  
    }  
}

Fabrizio Giudici replied on Tue, 2008/06/17 - 8:19am

I can't but agree with the other commenters: I really can't appreciate the advantage of the use of a closure in place of e.g. the example by Brian. Perhaps it's because I can't read the closure code? <g>

Ricky Clarkson replied on Tue, 2008/06/17 - 8:30am

Actually in this case an interface would be better, if it has a good name.  Also, the only closure you showed, { => true } would work fine if the interface had a single method taking no parameters and returning a boolean (see closure conversion in the BGGA spec).

The rest of the post was on function types, not closures, which are appropriate for writing very general methods (such as curry) or for writing ad-hoc code.  It's arguable that function types would not exist if generics supported primitives (you could write the above as Func<boolean> - that's how C# does it, though not many C# users use Func directly).

It's a shame that some people saw this bad code as evidence against closures.  It's really just you learning stuff, and is fine.

Dhananjay Nene replied on Tue, 2008/06/17 - 1:19pm

Dennis,

 I am not even sure if we are talking about closures here. From what I could understand probably what you want is "Function Objects". Unlike languages such as python / ruby java does not treat functions as first class objects. But there are reasonable approximations. Some people have already suggested interfaces which wrap a function. Two interfaces already within the JRE that do come to mind are Runnable and Callable which can be argued to have function object semantics though they also have a whole set of other semantics associated with threading (and hence not usable in this context). Combined with mock testing strategies this can help you work through otherwise impossible to reach testing scenarios. You can potentially also use libraries that support Functors - objects that represent functions. eg. Commons Functor (Example).

 Closures have many other semantics that are associated with them. An important one is the propogation of the context where the closure was declared into the situation where it was actually triggered. Thus the runtime variables that were available at the point in time the closure was declared, suddenly become available when it is triggered. I really do not see the above example suggesting the requirements for closures.

 Dhananjay

Comment viewing options

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