The Source for Java Technology Collaboration
User: Password:
Register | Login help    

Search

Online Books:
java.net on MarkMail:


Cay Horstmann

Cay Horstmann is author of Core Java (Sun Microsystems Press 1996-2009), Enterprise Java for Elvis (Sun Microsystems Press, to appear), and co-author of Core JSF (Sun Microsystems Press 2004-2009) Cay is professor of computer science at San Jose State University. He is a computer science series editor at Prentice-Hall and a frequent speaker at computer industry conferences. For four years, Cay was VP and CTO of an Internet startup that went from 3 people in a tiny office to a public company.

 

Cay Horstmann's blog

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 >> Blogs      J2SE      Open JDK      
Comments
Comments are listed in date ascending order (oldest first)

"... 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

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.

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.

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); 
Syndicate content