Click here to Skip to main content
15,887,485 members
Articles / Programming Languages / Java / Java SE / J2EE

Lambdaj 2.0 Brings (Almost) Real Closures to Java

Rate me:
Please Sign up or sign in to vote.
4.33/5 (2 votes)
6 Sep 2009Apache3 min read 14.3K   6  
How lambdaj tries to partially fill the lack of closures in Java

Introduction

Closures represent probably the most important feature that is missing in the toolbox of each Java programmer. Some programmers don't feel (or don't understand) the need for closures, while some others do and probably for this reason are evaluating migration towards a functional enabled programming language. The majority of us just learned to partially workaround this lack by using the verbose and poorly readable (anonymous) inner classes.

Actually the opportunity to provide Java with this feature has been vastly debated and for a certain amount of time, it seemed they were going to introduce it in Java 7 through the (in my opinion, awful) BGGA specification. In the end, they decided to give up leaving the Java developers still orphans of closures. 

lambdaj's Closure

lambdaj tries to partially fill this gap by introducing in its release 2.0 a new feature that allows to define, in its traditional DSL style, first-class functions with free variables like in the following example:
Java
Closure println = closure(); { of(System.out).println(var(String.class)); } 
I believe it is straightforward to understand what the println closure does. In particular, the var() method binds a free variable of type String to the closure. Moreover, note that the curly brackets around the statement that define the behavior of the closure are only syntactic sugar and they could be safely removed even if you find the code more readable by keeping them. You can then invoke this closure by "closing" its free variable once:
Java
println.apply("one"); 

or more times:

Java
println.each("one", "two", "three"); 

As you can expect, this last statement will cause the Strings "one", "two" and "three" to be printed on 3 different lines of the Java standard output. It is possible to create both untyped (as in the former example) and strongly typed closures. Supposing your classes have a method that sums 2 ints: 

Java
public int sum(int a, int b) {
    return a + b;
} 

It is possible to instance a closure that invokes this method as it follows:

Java
Closure2<Integer,Integer> adder = closure(Integer.class, Integer.class); {
    of(this).sum(var(Integer.class), var(Integer.class)); 
} 

While you were allowed to call the first closure with any type and number of variables (it will eventually throw an Exception if invoked in a wrong way), you can invoke this second one only by passing 2 ints to it as expected:

Java
int result = (Integer)adder.apply(2, 3); 

Curry 

Another feature typically available on closures is the so called curry that allows to create another closure by fixing the value of some free variables. For example, you can have a closure of one free argument that adds 10 to any number by doing a curry of the second variable of the former closure as it follows:

Java
Closure1<Integer> adderOf10 = adder.curry2(10); 

In this way, by invoking this last closure with the value 3...

Java
int result = (Integer)adderOf10.apply(3); 

... you will obtain 13 as expected. In the end, note that you could achieve exactly the same result by directly creating a closure with only one free parameter and the second one already fixed to 10 as in this last statement: 

Java
Closure1<Integer> adderOf10 = closure(Integer.class, Integer.class); { 
    of(this).sum(var(Integer.class), 10); 
}

Why Are Closures Useful?

If you still don't see how closures can be useful for you, let me make a slightly more complex example that could show why I think that they are the most powerful tool in order to generalize your code and thus avoid duplications. For this purpose, let's write a method that reads a file from the classpath and then prints its content line by line on the Java standard output:

Java
public void printFile(String fileName) {
    BufferedReader reader = null; 
    try {
        InputStream stream = getClass().getClassLoader().getResourceAsStream(fileName);
        reader = new BufferedReader(new InputStreamReader(stream));
        for (String line = reader.readLine(); 
		line != null; line = reader.readLine()) {
            System.out.println(line); 	// This is actually the only 
					// meaningful statement in this method
        } 
     } catch (IOException ioe) {
        throw new RuntimeException("Error while reading file " + fileName, ioe);
     } finally {
         try {
             if (reader != null) reader.close();
         } catch (IOException ioe) {
             throw new RuntimeException("Error while closing file reader", ioe);
         } 
     } 
}  

There are lots of bloatware and maybe just one meaningful line of code in this method, isn't it? But now suppose you also need a method that writes the file into a String and another one that just counts the number of non-empty lines in the file itself. Instead of copying and pasting the former method another two times and changing just a single statement in each new method, I think that it could be a better idea to generalize it, by passing a closure that tells the method, case by case, how a line read from the file should be managed, as follows:

Java
public void printFile(String fileName) {
    Closure1<String> lineReader = closure(String.class); 
	{ of(System.out).println(var(String.class)); }
    readFileByLine(fileName, lineReader);
} 

public String readFile(String fileName) {
    StringWriter sw = new StringWriter();
    Closure1<String> lineReader = closure(String.class); 
	{ of(sw).write(var(String.class)); }
    readFileByLine(fileName, lineReader);
    return sw.toString();
} 

public int countFileLines(String fileName) {
    lineCounter = 0;
    Closure1<String> lineReader = closure(String.class); 
	{ of(this).countNonEmptyLine(var(String.class)); }
    readFileByLine(fileName, lineReader);
    return lineCounter;
} 

private int lineCounter = 0;
void countNonEmptyLine(String line) {
    if (line != null && line.trim().length() > 0) lineCounter++;
} 

private void readFileByLine(String fileName, Closure1<String> lineReader) {
    BufferedReader reader = null;
    try {
        InputStream stream = getClass().getClassLoader().getResourceAsStream(fileName); 
        reader = new BufferedReader(new InputStreamReader(stream));
        for (String line = reader.readLine(); 
	line != null; line = reader.readLine()) {
            lineReader.apply(line);
        } 
     } catch (IOException ioe) {
        throw new RuntimeException("Error while reading file " + fileName, ioe);
     } finally {
        try {
            if (reader != null) reader.close();
        } catch (IOException ioe) {
            throw new RuntimeException("Error while closing file reader", ioe); 
        } 
     } 
}

History

  • 6th September, 2009: Initial post

License

This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0


Written By
Switzerland Switzerland
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
-- There are no messages in this forum --