Closures
A closure is a form of anonymous function. It is declared using the following syntax:
{ formal parameters => statements expression }. Formal parameters, statements,
and expression are optional in this clause. For example, { int x => x + 1 }
is
a function that takes a single int argument and returns its value incremented by 1. A closure can be invoked
by the invoke method. For example, { int x => x + 1 }.invoke(10)
will call the closure with argument 10.
public class SimpleClosure {
public static void main(String[] args) {
// function with no arguments; return value is always 42
int answer = { => 42 }.invoke();
System.out.println(answer);
}
}
A closure with one argument:
double log = { double x => Math.log(x) }.invoke(10);
A closure with a statement:
// this will print "31 is odd" and return 15
int half = {
int x =>
if (x % 2 != 0) System.out.printf("%d is odd%n", x); x / 2
}.invoke(31);
A closure with two arguments:
int sum = { int x, int y => x + y }.invoke(3, 4); // will return 7
A closure does not have to return any value (the function may have return type void):
{ char c => System.out.println(c); }.invoke('@'); // will print @
A closure that returns a string:
String reversed = {
String s =>
new StringBuilder(s).reverse().toString()
}.invoke("abcd"); // will return "dcba"
A closure that returns an instance of Runnable.
{ => new Runnable() {
public void run() {
System.out.println("hi from Prague");
}
}
}.invoke().run(); // invoke() returns an instance of Runnable and we
// call run() on it immediately
We can declare local variables in closures:
{ int n =>
int m = n + 1; System.out.println(m * m);
}.invoke(3); // will print 16
Implementation issue
This paragraph describes how the prototype compiler implements closures.
For each closure, the compiler will generate an interface with single method invoke()
.
E.g., for closure { => 42 }
, the interface will look like:
public interface I {
int invoke(); // no arguments and return type int
}
And for closure { int x, int y => x + y }, the interface will be like:
public interface III {
int invoke(int x, int y); // two int arguments and return type int
}
The interface will be in the javax.lang.function
package. Then the compiler will generate an anonymous
subclass of this interface, create an instance of this subclass, and call the invoke method.
For example, the code
public static void main(String[] args) {
int answer = { => 42 }.invoke();
System.out.println(answer);
}
will be tranformed approximately as follows:
public static void main(String[] args) {
int answer = new javax.lang.function.I() {
public int invoke() {
return 42;
}
}.invoke();
System.out.println(answer);
}
So, at runtime a closure is represented as object. In JSR, this object is called the closure object.
Exercises
- Write a closure that returns the lesser of two double values.
- Write a closure that decides if a value is even.
- Write a closure that returns last n characters of a string.
Solutions
double min1 = { double x, double y => x < y ? x : y }.invoke(7.5, 8.2);
double min2 = {
double x, double y =>
double m; if (x < y) m = x; else m = y; m
}.invoke(10.22, 9.76);
boolean even = { int x => x % 2 == 0 }.invoke(15);
{ String, int => String } lastNChars =
{ String s, int n => int len = s.length(); s.substring(len - n, len) };