Skip to main content

Closures? In Java 7???

Posted by cayhorstmann on November 18, 2009 at 8:22 PM PST

Today, a tantalizing announcement by Mark Reinhold about closures in Java 7 has made its way through the twittersphere.

On the same day, Neal Gafter updated his closure proposal (known as the BGGA proposal, named after the initials of Bracha, Gafter, Gosling, and von der Ahé, and not at all related to the B. G. G. A. organization).

Presumably the timing is not a coincidence.

The proposal is a bit technical, so I thought I'd translate my understanding of it into some use cases. Here goes.

  • Running something on a thread pool:
    pool.submit(#() { for (int i = 1; i < 1000000; i++) doWork(i) });

    Here, the #() { ... } denotes a function literal. #() indicates that the function has no parameters. (I guess the return type is inferred.) The function body is a block, enclosed in braces.

    If you squint really hard, the # looks like a λ :-)

    The function object is automatically converted to a Runnable because the Runnable interface has a single method, also with no parameters.

  • Adding a button listener:
    button.addActionListener(#(ActionEvent e) System.out.println("Hi!));

    This is pretty much the same as the previous example, except that you can have a single expression after the #(...), and then you don't use braces.

    It is certainly an improvement over

    button.addActionListener(
       new ActionListener() {
          public void actionPerformed(ActionEvent e) {
             System.out.println("Hi!"); } } );
  • Sorting with a comparator:

    Here we sort a string list by increasing length:

    Collections.sort(strings, #(String a, String b) a.length() - b.length());

    which can also be written as

    Collections.sort(strings, #(String a, String b) { return a.length() - b.length(); });

    Note that you need a return and a ; if you use a block instead of an expression.

  • Using a java.util.concurrent lock:
    withLock(myLock, #() {
       if (account2.getBalance() < amount) return;
       account1.deposit(amount);
       account2.withdraw(amount);
    });

    Here we assume that some friendly soul has written a withLock method. You can find the code for that method in Neal's proposal. I am purposefully not reproducing that code because it will frighten small children. But so what—it's something that only library writers worry about.

    This is similar to my first example, except for the return statement. In prior versions of the BGGA proposal, return could return from the method containing the withLock method. That was nifty in a way—it allowed new control statements that feel just like the built-in ones. But it was also potentially confusing. Now return simply returns from the function, and execution continues with the statement following withLock.

  • Filtering a collection:
    double amount = 1000;
    Collection<Account> result = filter(accounts, #(Account a) a.getBalance() < amount);

    Again, I assume that some friendly soul has written a filter method.

    Note that the closure can reference the amount variable from the enclosing scope. It doesn't have to be final because I am never assigning to it after I initialize it.

  • Updating a variable in the enclosing scope:
    @Shared int clickCount = 0;
    button.addActionListener(#(ActionEvent e) { clickCount++; });

    The @Shared is required because the clickCount variable is mutated.

    By the way, this is another one of those things that is incredibly convoluted in Java without closures. int is a primitive type and Integer is immutable. The path of least resistance is

    final int[] clickCount = new int[1];
    button.addActionListener(
       new ActionListener() {
          public void actionPerformed(ActionEvent e) {
             clickCount[0]++; } } );

Overall, I like it. The simple things are simple, and there don't seem any hidden pitfalls.

Of course, someone out there will say "Oh my, oh my, that #(ActionEvent e){...} makes my head explode. It's not cuddly and comfortable like new ActionListener() { public void actionPerformed(ActionEvent e) {...}}”.

To which I'll say “I rest my case”.

More interestingly, someone on the Project Coin mailing list tried to muddy the waters by asking how this feature interacts with other new features of Java 7, like

try (Closeable c = #() { System.out.println("YOUR HEAD A SPLODE"); }) {
   // ...
}

Ok, that means that the close method of c will be invoked in the finally clause, and that method is set to the #() {...}. What's so hard about that?

If this is indeed to come to pass in Java 7 (and I have absolutely no inside knowledge whether it will), I am willing to wait a bit longer. What do you think?

Related Topics >>

Comments

The use of the # sign

The use of the # sign is going to conflict with use of the C++ preprocessor on Java files (for those of us who find that necessary). Couldn't they have found a better syntax!? How about just leaving it out?

button.addActionListener( (ActionEvent e) { System.out.println("Hi!); } );

Apart from that, it's a nice idea for improving Java's agonizingly long-winded syntax. Now all we need are unsigned integers and the with block.

The import javax.faces.model.ManagedBean cannot be resolved

Hi Cay, I used example as per: http://weblogs.java.net/blog/cayhorstmann/archive/2009/01/a_simple_jsf2a... but I am getting error while compilation. The error is: The import javax.faces.model.ManagedBean cannot be resolved Please let me know the jar files needed for this. Current jar files in classpath are: jsf-api.jar jsf-impl.jar jstl.jar standard.jar servlet-api.jar I am new to JSF. Please help me out. Thanks, Vikash Anand.

It's about time

It's about time that the Java language people started adding some of the features that have been being added to .NET. I don't understand why they don't just whole sale copy it from .NET. Why reinvent the wheel, only differently? Afterall, Microsoft copied Java to begin with. Java should do the same to .NET. There are a lot of other features in .NET that I would like to see added to Java also. Like properties, object initializers, and numerous other things. It would be great if they had something like LINQ also.

"... it's something that only library writers worry about."

Cay,

Can you back that hypothesis, at least with anecdotes? My experience says the opposite.

Thanks,

Dave

Sure, I am always good for an

Sure, I am always good for an anecdote :-) I have been teaching Scala for a couple of years, and students use closures extensively--how could they not? They had little trouble with them and find them quite natural. They never tried writing the kind of generic code with closures that looks like alphabet soup (in Scala or Java).

Latest Spec??

FYI: the 0.6a specification http://www.javac.info/closures-v06a.html is NOT BGGA, nor is it intended to document Mark Reinhold's plans for JDK7. Although it resulted from a discussion with the other authors of the BGGA spec culminating in this document a couple of weeks ago, I do not have permission to list any of them as authors (I asked). The spec was completed before Devoxx but not linked on javac.info until Wednesday. In fact, I was attending PDC09 when I read a tweet about closures and put up the link. I think the timing and similarity to Mark Reinhold's announcement are either coincidental or Gosling has passed it on to him. I've had no contact with Mark Reinhold, so I have no idea which. Note that this spec allows access to non-final local variables from the enclosing scope, which rumors suggest Reinhold has ruled out.

Thanks for letting me know--I

Thanks for letting me know--I should have checked with you first; sorry about not doing so. Well, your proposal is reasonable and a good contribution to the discussion. If Mark has nothing but a couple of slides with no concrete plans behind it, that's very unfortunate. And if so, on what basis does he rule out access to non-final local variables? Even CICE supported them. It's really time for the other shoe to drop from Sun.

Clean up Generics first

Closures is going to turn into another Generics mess. How about you clean up Generics first (making them more readable through Reification) before you stuff Closures in there? I would love to see an entire release dedicated to *improving* the language cohesiveness, not reducing it. And for god's sake, feel free to drop backwards compatibility with software that is over 10 years old. No one cares. Really!

Yes they do!

We still have customers holding on to Java 1.4 environments with no desire to upgrade.

oh yark. It's not hard to

oh yark. It's not hard to understand, but I only see more problems for junior developers or when we will use IDE. Imagine that you want to find all Runnable into your classes, you will miss all the line using #. For what I have seen, it's not worth it.. not yet.

bye bye Java

Hello Obfuscated Java Programming Contest.
So we get function pointers, and destroy any semblance of writing maintainable, readable, code in the process.
Seems Sun bowed once again to the Me2! crowd of people who think that anything and everything that's in any other language would be a great addition to Java and that "Jav iz ded" without it.

Start a new fork?

I agree -- Java 7 with closures is an absolute deal-breaker. I will simply never allow any of my teams to use it.

At the same time, I'd hate to be stuck on Java 6 forever. How much interest out there to fork the language to have Java 7 - closures + CICE/ARM? I would be very interested in contributing to such a project.

If you have in-house rules on

If you have in-house rules on what syntax your teams can use then you should already be incorporating Checkstyle into your (ideally, automated) build process. I'm certain that once Java 7 is out, you'll be able to add a rule to disallow the closure syntax.

Calm down, the sky isn't falling

Today, people write obfuscated Java code every day. Code such as

button.addActionListener(
   new ActionListener() {
      public void actionPerformed(ActionEvent e) {
         // button action
} } );

When people read this code, do they say "Oh, here we design a new inner class that implements the ActionListener interface so that its actionPerformed method carries out this action"? Or do they say "When the button gets clicked, this action happens, and I have to read past this !@#$ noise because Java makes me"? The closures version
button.addActionListener(#(ActionEvent e) { /* button action */ });

is less obfuscated.

yes, but...

Yes, the closures version is arguably slightly less obfuscated.

But I think the argument is not "which is cleaner in the common, best-case scenario", but rather "which gives more ability to write really nasty code?"

Take generics, for example. Yes, used reasonably (which is most cases), they eliminate ugly type casting. But the bad cases - wildcards and such - also introduces a potentially huge ability to write some *really* obfuscated code. Not just "It looks a bit funny but I see what it's doing" but "I'm an expert programmer and can't make heads or tails of it".

Or take a look at many of the often-misused C++ constructs: function pointers, operator overloading, templates, even pointers or gotos. In all those cases, the simple, straightforward usage really is cleaner than the alternative. But the sheer horror of dealing with the misuses is tough to get over.

nice summary

Very nice summary - thank you. As someone who's head easily explodes when looking at closure proposals, I can say that this seems like a nice, big step in the right direction.

One thing I wonder is what's the benefit to allowing us to drop the curly braces when we just have an expression? Wouldn't it be cleaner to just force people to write include a return statement? I'd rather see "{return a+b;}" rather than just "a+b". I suppose I've never gotten over the complaint that you don't have to use curly braces on if/then/else clauses if there's only one statement, and this is similar.

You raise a good point with

You raise a good point with the syntax of "expr" vs. { return expr; }. Some programming languages want to guide the user with strict rules, eliminating as much gratuitous choice as possible. For example Python with using indentation for blocks. No brace style wars in Python! Other languages try to give you every option and shortcut imaginable, for example Scala. Java is somewhere in the middle, and I think leaving this choice is within the spirit of Java.

@Shared versus multiple threads

Assuming that I have @Shared variable and pass closure modifying it to another thread for execution, what will be the visibility of updates? Could you define local volatile @Shared variable? How it would be implemented ? (maybe instead of using array[1], there would be class of mutable wrappers for each primitive type, with second set of volatile counterparts, sharing same interface)

My guess is that you will be

My guess is that you will be able to define a @Shared volatile int. They will just have to do the right thing under the hood--such as transforming it into a new int[1] and using some helper method in sun.misc.Unsafe for reading/updating the entry. Another reason why you want closures in the language rather than as an inner-class coding convention :-)

Very Good News

Whatever they finally pick, it's good that the closure wars are over and we can move on. The next decade is coming soon :-)

Nothing hard about it

Cay, The original 'muddying the waters' snippet was also preceded by a comment declaring that it's a tongue-in-cheek example, with its 'Closeable closure' (in the original example) and deliberate mix of ARM and the simplified BGGA syntax. I agree that there's nothing hard about it, although I have no idea at this point whether it would actually be considered valid. Investigating how the features interact is important though, and isn't muddying anything in the context of the Coin list. Mark

You are right--it is

You are right--it is important to understand how the features mix. The "muddying the waters" comment was meant to be tongue-in-cheek as well :-)

Fair enough then :) I

Fair enough then :) I certainly agree that it's something worth waiting a little longer for. Let's see what unfolds...

I'm confused. Some people are

I'm confused. Some people are saying that its more like BGGA others like FCM. Non local returns yea or not? Also please don't do this http://blogs.msdn.com/ericlippert/archive/2009/11/12/closing-over-the-lo...

We don't really know what

We don't really know what exactly Sun intends to do. I simply analyzed the BGGA 0.6a proposal, which is presumably what BGGA is today. There is no denying that it looks a lot like FCM. No non-local returns. # for lambda.

Since you need to annotate a mutated variable with @Shared when you capture it, perhaps people would think twice before writing

foreach(@Shared String v : values) 
   funcs.add( #() => v);