Library tutorials & articles

The Beauty of Closures

Comparing capture strategies: complexity vs power

Clearly the Java scheme is more restrictive, but it does make life significantly simpler, too. Local variables behave in the same way they've always done, and in many cases the code is easier to understand, too. For example, look at the following code, using the Java Runnable interface and the .NET Action delegate - both of which represent actions taking no parameters and returning no value. First let's see the C# code:

// In Example3a.cs
static void Main()
{
    // First build a list of actions
    List<Action> actions = new List<Action>();
    for (int counter = 0; counter < 10; counter++)
    {
        actions.Add(() => Console.WriteLine(counter));
    }

    // Then execute them
    foreach (Action action in actions)
    {
        action();
    }
}

What's the output? Well, we've only actually declared a single counter variable - so that same counter variable is captured by all the Action instances. The result is the number 10 being printed on every line. To "fix" the code to make it display the output most people would expect (i.e. 0 to 9) we need to introduce an extra variable inside the loop:

// In Example3b.cs
static void Main()
{
    // First build a list of actions
    List<Action> actions = new List<Action>();
    for (int counter = 0; counter < 10; counter++)
    {
        int copy = counter;
        actions.Add(() => Console.WriteLine(copy));
    }

    // Then execute them
    foreach (Action action in actions)
    {
        action();
    }
}

Each time we go through the loop we're said to get a different instance of the copy variable - each Action captures a different variable. This makes perfect sense if you look at what the compiler's actually doing behind the scenes, but initially it flies in the face of the intuition of most developers (including me).

Java forbids the first version entirely - you can't capture the counter variable at all, because it's not final. To use a final variable, you end up with code like this, which is very similar to the C# code:

// In Example3a.java
public static void main(String[] args)
{
    // First build a list of actions
    List<Runnable> actions = new ArrayList<Runnable>();        
    for (int counter=0; counter < 10; counter++)
    {
        final int copy = counter;
        actions.add(new Runnable()
        {
            public void run()
            {
                System.out.println(copy);
            }
        });
    }

    // Then execute them
    for (Runnable action : actions)
    {
        action.run();
    }
}

The meaning is reasonably clear with the "captured value" semantics. The resulting code is still less pleasant to look at than the C# due to the wordier syntax, but Java forces the correct code to be written as the only option. The downside is that when you want the behaviour of the original C# code (which is certainly the case on occasion) it's cumbersome to achieve in Java. (You can have a single-element array, and capture a reference to the array, then change the value of the element when you want to, but that's a nasty kludge).

Comments

  1. 26 Aug 2009 at 22:44

    Thanks Jon for a clear well-written article. I reached this article after downloading Ruby, and wanted to brush up on closures and some other supported language features. I appreciate the full code examples- many times authors leave out details, which can make the reader feel as if he or she is "missing something". Best Regards, Mark

  2. 13 Nov 2008 at 03:43
    Dear Jon, Thanks for taking the time to writing this great introduction to closures in C# and Java. By the way, have you had the chance to look at the [Umbrella project](http://www.codeplex.com/umbrella) (via CodePlex)? It's a .NET library that offers many helper/utility methods to easily work around various objects, pretty much using the same approach as your article. Though the code isn't highly documented, it's written explicitly enough to understand it. It's one of those libraries that should belong in everyone's lib folder ;) Brian www.BrianOnSoftware.com

Leave a comment

Sign in or Join us (it's free).

Jon Skeet C# MVP currently living in Reading and working for Google.
AddThis

Related podcasts

  • Stack Overflow: Podcast #29

    This is the twenty-ninth episode of the StackOverflow podcast, wherein Joel and I discuss the following: The downside of being a PC gamer: it's prime game release season. My productivity last week was nil due to the release of Fallout 3, as I discuss on my blog. But it was totally ...

Events coming up

  • Nov 17

    Portland Java User Group

    Portland, United States

    This month's topic: TBD----------PJUG meetings start with eat+meet+greet time (pizza and beverages are provided), followed by the featured speaker, then some time for Q&A, discussion, and sometimes a drawing to give away swag. :)It is...

Want to stay in touch with what's going on? Follow us on twitter!