Usage
In this post, I express my opinion on many places. For example, when I say "it is more intuitive...", I mean "in my opinion, it is more intuitive...".
As we already mentioned, closures are transformed to anonymous inner classes at compile time. So, all we can do with closures, can be done with inner classes as well. However, the access to local variables may require a little effort. For example, the code
int x = 4; { => System.out.println(++x); }.invoke();
can be rewritten without closures as follows:
final int[] x = { 4 }; new javax.lang.function.V() { public void invoke() { System.out.println(++x[0]); } }.invoke();
Even if closures do not increase the power of language, in many cases they are more concise and intuitive than other means. They are a natural choice when we want to parameterize an algorithm with a function. For example, sorting may be parameterized with a comparison function:
Integer[] primes = { 19, 23, 2, 11, 17, 31, 5, 13 }; Arrays.sort(primes, { Integer x, Integer y => y.compareTo(x) });
Here, it is more intuitive to use a function argument than the
Comparator
interface because the purpose of the argument is only
to compare two values.
One should also prefer closures for predicates because predicate is a function:
static <T> void selectAndPrint(Iterable<T> items, { T => boolean } cond) { for (T item: items) { if (cond.invoke(item)) { System.out.println(item); } } } public static void main(String[] args) { List<String> cities = Arrays.asList( "Prague", "San Francisco", "Moscow", "New York", "Paris"); int max = ...; // will print cities that are not longer than max selectAndPrint(cities, { String s => s.length() <= max }); char c = ...; // will print cities that contain character c selectAndPrint(cities, { String s => s.indexOf(c) >= 0 }); }
Function types can also be used to describe the input of algorithm. For example, a method that returns the size of area under the function curve should have a function argument:
static double determineFiniteIntegral( double from, double to, { double => double } func) { double area; //... return area; }
Another usage is in a method that takes a block of code as argument. For example, the execute
method
takes a block that should be performed in a separate thread:
ExecutorService pool = Executors.newFixedThreadPool(5); pool.execute({ => System.out.println("thinking..."); });
Again, it is more intuitive and logical to pass a block of code than an object that implements Runnable
.
Closures enable us to write methods that behave in a similar way as the foreach loop:
static void forEach(int[] nums, { int => void } block) { for (int n: nums) { block.invoke(n); } } public static void main(String[] args) { int[] p = { 5, 1, 3, 4, 2 }; int sum = 0; forEach(p, { int n => sum += n; }); // will sum up the numbers in array int min = Integer.MAX_VALUE; forEach(p, { int n => if (n < min) min = n; }); // will find the minimum }
For example, we can implement a method that performs an operation on each map entry:
static <K, V> void forEachEntry(Map<K, V> map, { K, V => void } block) { for (Map.Entry<K, V> entry: map.entrySet()) { block.invoke(entry.getKey(), entry.getValue()); } } public static void main(String[] args) { Map<Integer, String> map = new HashMap<Integer, String>(); //... forEachEntry(map, { Integer key, String value => System.out.println(key + ": " + value); }); }
We can also use closures to implement the visitor design pattern in a simple fashion:
class Node { int value; Node left, right; } class Tree { Node root; //... void visitPreOrder({ int => void } block) { visitPreOrder(root, block); } void visitPreOrder(Node p, { int => void } block) { if (p != null) { block.invoke(p.value); // first perform the operation on this node visitPreOrder(p.left, block); // then on the left subtree visitPreOrder(p.right, block); // and then on the right subtree } } } public class TreeOperations { public static void main(String[] args) { Tree tree = ...; List<Integer> nums = new ArrayList<Integer>(); // will add the numbers to the list tree.visitPreOrder({ int v => nums.add(v); }); //... } }