Originally posted by Kuciwalker
View Post

Kuci, I cannot explain this any better. Closures do not exist in Java. This is according to virtually everyone. James Gosling states they do not exist in Java. Mark Reinhold states they do not exist in Java. The entire ****ing internet states they do not exist in Java.
Closures can serve as anonymous functions/classes, but they ARE NOT anonymous functions/classes.
Here is an article by a Senior Sun engineer:
http://www.ibm.com/developerworks/java/library/j-jtp04247.html
The Java language provides inner classes, which can contain references to fields of their enclosing object. This feature is richer than function pointers because it allows the inner class instance to retain a reference to the environment in which it was created. Indeed, at first glance, inner classes seem to provide most, if not all, of the value of closures; one could easily construct an interface called UnaryFunction and create a memoizing wrapper that can memoize any unary function. But this approach is often unwieldy in practice; it forces all the code that interacts with your function to be written with an awareness of this function "framework."
Anonymous classes let you create objects that capture part of the environment in which they were defined, but objects and blocks of code are not the same thing. As an example, consider any repetitive coding pattern, such as executing a block of code with a Lock held. If we want to increment a counter with a Lock held, the code looks like Listing 2 -- frustratingly verbose for such a simple operation:
Listing 2. Canonical idiom for executing a block of code with a lock held
lock.lock();
try {
++counter;
}
finally {
lock.unlock();
}
It would be nice to abstract out the lock management code, which would make the code more compact and less error-prone. As a first attempt, you might create a withLock() method like Listing 3:
Listing 3. Attempt at abstracting the concept of "do this while holding lock," which suffers from a lack of exception transparency
public static void withLock(Lock lock, Runnable r) {
lock.lock();
try {
r.run();
}
finally {
lock.unlock();
}
}
Unfortunately, this approach only takes you part of the way to where you want to be. One of the goals of creating this abstraction was to make the code more compact; unfortunately, the syntax of anonymous inner classes is not very compact, and the calling code will look something like Listing 4:
Listing 4. Client code for the withLock() method in Listing 3
withLock(lock,
new Runnable() {
public void run() {
++counter;
}
});
That's still a lot of code just to increment a counter with a lock held! Further, the abstraction barrier introduced by turning the block of code that is guarded by a lock into a method invocation complicates matters severely -- what happens if the guarded block of code might throw a checked exception? Now we cannot use Runnable as our task representation; we must create a new representation that allows exceptions to be thrown across the method invocation. Unfortunately, generics don't help us very much here either; while you can create a method that has a generic type parameter E identifying a checked exception it might throw, this approach doesn't generalize well to methods that throw more than one checked exception type (which is why the call() method in Callable is declared to throw Exception, rather than a type specified by a type parameter). The approach in Listing 3 is hampered by a lack of exception transparency. It also suffers from other forms of nontransparency; statements like return or break would mean something different in the context of the Runnable in Listing 4 than they would in the try block in Listing 2.
Ideally, you'd like to be able to have your guarded increment operation look something like Listing 5 and have the code in the block mean the same as it would in the expanded form from Listing 2:
Listing 5. Ideal (but hypothetical) form for client code for Listing 3
withLock(lock,
{ ++counter; });
By adding closures to the language, it becomes possible to create methods that can behave like control flow constructs, such as "execute this code with a lock held," "operate on this stream and close it when you're done," or "time how long it takes to execute this block of code." This strategy has the potential to simplify certain types of code that repeatedly use particular coding patterns or idioms, such as the locking idiom in Listing 2. (Another technique that offered similar expressiveness, to a certain extent, was the C preprocessor; it is possible to express the withLock() operation as a preprocessor macro, though macros are harder to compose and less safe than closures.)
Anonymous classes let you create objects that capture part of the environment in which they were defined, but objects and blocks of code are not the same thing. As an example, consider any repetitive coding pattern, such as executing a block of code with a Lock held. If we want to increment a counter with a Lock held, the code looks like Listing 2 -- frustratingly verbose for such a simple operation:
Listing 2. Canonical idiom for executing a block of code with a lock held
lock.lock();
try {
++counter;
}
finally {
lock.unlock();
}
It would be nice to abstract out the lock management code, which would make the code more compact and less error-prone. As a first attempt, you might create a withLock() method like Listing 3:
Listing 3. Attempt at abstracting the concept of "do this while holding lock," which suffers from a lack of exception transparency
public static void withLock(Lock lock, Runnable r) {
lock.lock();
try {
r.run();
}
finally {
lock.unlock();
}
}
Unfortunately, this approach only takes you part of the way to where you want to be. One of the goals of creating this abstraction was to make the code more compact; unfortunately, the syntax of anonymous inner classes is not very compact, and the calling code will look something like Listing 4:
Listing 4. Client code for the withLock() method in Listing 3
withLock(lock,
new Runnable() {
public void run() {
++counter;
}
});
That's still a lot of code just to increment a counter with a lock held! Further, the abstraction barrier introduced by turning the block of code that is guarded by a lock into a method invocation complicates matters severely -- what happens if the guarded block of code might throw a checked exception? Now we cannot use Runnable as our task representation; we must create a new representation that allows exceptions to be thrown across the method invocation. Unfortunately, generics don't help us very much here either; while you can create a method that has a generic type parameter E identifying a checked exception it might throw, this approach doesn't generalize well to methods that throw more than one checked exception type (which is why the call() method in Callable is declared to throw Exception, rather than a type specified by a type parameter). The approach in Listing 3 is hampered by a lack of exception transparency. It also suffers from other forms of nontransparency; statements like return or break would mean something different in the context of the Runnable in Listing 4 than they would in the try block in Listing 2.
Ideally, you'd like to be able to have your guarded increment operation look something like Listing 5 and have the code in the block mean the same as it would in the expanded form from Listing 2:
Listing 5. Ideal (but hypothetical) form for client code for Listing 3
withLock(lock,
{ ++counter; });
By adding closures to the language, it becomes possible to create methods that can behave like control flow constructs, such as "execute this code with a lock held," "operate on this stream and close it when you're done," or "time how long it takes to execute this block of code." This strategy has the potential to simplify certain types of code that repeatedly use particular coding patterns or idioms, such as the locking idiom in Listing 2. (Another technique that offered similar expressiveness, to a certain extent, was the C preprocessor; it is possible to express the withLock() operation as a preprocessor macro, though macros are harder to compose and less safe than closures.)
Comment