Lambdas: coming to a Java 8 near you!


A brief introduction to the lambda expressions, proposed for inclusion into Java 8.

Tue 06 December 2011

What are Lambdas?

A lambda expression is a type of anonymous function, that can be written inline within a method and used wherever expressions are used. Sometimes you might find them referred to as closures, though there are some caveats to that reference that I explain below. Like a normal Java method it has parameters and a body that can be executed. One of the exciting new features in Java 8 are lambda expressions, which are being defined as part of JSR 335.

Adding lambda expressions will to allow Java to support functional programming techniques and higher order functions more easily. This isn't to say that is suddenly going to overnight become an exotic and esoteric language such as Haskell, but it'll be moving in the same direction as languages such as C#, Ruby or python. That is to say, offering partial support for a functional style where it makes sense - but still letting you use imperative techniques, such as mutable variables.

Basic Syntax

Here is a lambda expression that increments a number by 1:

            x -> x + 1
        

As you can see the expression starts with a parameter and ends with it's body, with an arrow splitting the two up. You can explicitly type your parameter as well, put type annotations on the parameter or have multiple parameters. If you wish to do these things, you'll need to surround your arguments in brackets, for example:

            // Explicit Types
            (Integer x) -> x + 1
            // Multiple Arguments
            (Integer x1, Integer x2) -> x1 + 1
            // Annotations:
            (Integer x1, @SuppressWarnings("unused") Integer x2) -> x1 + 1
        

So far all the lambda bodies have been expressions, but you can also write the body as a sequence of statements, like a normal method.

        (x) -> { 
            x += 5; 
            System.out.println(x); 
            return x; 
        };
        

Since lambdas are normal expressions, you can treat them as such. For example you can assign them to variables, pass them as arguments to other methods.

        // a method: 
        public static void useFunc(IntFun f) { 
            System.out.println(f.apply(2)); 
        } 
        
        
        // in code 
        IntFun inc = x -> x + 1; 
        useFunc(inc); 
        useFunc(x -> x + 1); 
        

Types?

Since this is Java, all these functions are still statically typed, even if not all of those 'x' parameters are marked as being 'Integer'. Thankfully the introduction of method handles in Java 7 provides a way of referring to a method itself. This means that your functions don't all need to implement some common interface, they can be of anything that is deemed a 'Function Interface'

To quote the draft specification, "A functional interface is an interface that has just one abstract method, and thus represents a single function contract." These may also be referred to by the acronym 'SAM' which stands for 'Single Abstract Method'. For example:

        interface Runnable { void run(); } 
         
        interface Function { public R apply(A a); } 
         
        interface IntFun extends Function {} 
         
        // equals is defined by java.lang.Object, so this only has one abstract method 
        interface Comparator { 
            boolean equals(Object obj); 
            int compare(T o1, T o2); 
        } 
        
Consequently any API that is currently written/being written can make itself usable by lambdas by ensuring that it uses interfaces that maintain these constraints. One example API that already encourages a limited style of functional programming are the Guava libraries.

More interesting examples

Experienced functional programmers will be aware of the map function, that returns you a new list with every value altered by a function. In the Guava world this function is called 'transform'. Here is an example of some Java code that you can write today that uses it to increment a list:
        Collection result = transform(newArrayList(1, 2, 3), new Function() { 
            @Override 
            public Integer apply(Integer x) { 
                return x + 1; 
            } 
        }); 
        

This is an example of a higher-order function, ie a function that takes a function as an argument, or returns a function. The transform function abstracts away the process of iterating over the collection and building a new collection up, allowing for more abstract, high-level, code to be written. Unfortunately the readability advantages are somewhat overcome by a lot of line noise that is forced upon us, but if you rewrite it using lambdas:

        Collection result = transform(newArrayList(1, 2, 3),x -> x + 1);
        

Another commonly used higher order function is the filter function. If you give this a collection and a predicate, it returns a new collection containing the elements from the old collection that the predicate held true for. Here's a code example written in both the anonymous inner class style, and the lambda expression way.

        Collection threeAndFive = filter(newArrayList(1, 3, 5), new Predicate() { 
            @Override 
            public boolean apply(Integer x) { 
                return x > 2; 
            } 
        }); 
         
        Collection threeAndFiveByLambda = filter(newArrayList(1, 3, 5),x -> x > 2); 
        

Method References

You may, of course, have an existing codebase with many methods already defined, which you wish to use in place of lambdas. This is also possible presently in Java, using anonymous inner classes, but is equally as cumbersome. As well as providing lambda expressions, JSR 335 proposes some syntactic sugar in order to make this process easier. You can use the '::' symbol as a method reference for existing methods that you've written. Here's the previous example, but using a method reference:
        // Existing method 
        public static boolean greaterThanTwo(Integer x) { 
            return x > 2; 
        } 
         
        // prints [3, 5] 
        System.out.println(filter(newArrayList(1,3,5),LambdasExample::greaterThanTwo)); 
        

Are they really Closures?

In the introduction, I mentioned that lambda expressions might be referred to as closures. What that means is that a function is able to reference variables outside of it's normal scope, or is Closed Over the free variables of the surrounding scope.

        int value = 5; 
         
        IntFun addValue = x -> x + value; 
        

In this code example the 'addValue' function is able to refer to the variable named 'value' in the surrounding scope, though there is a caveat to this. If you've written anonymous inner classes before then you'll recall that they can only refer to surrounding variables that are marked as being final, ie they aren't assigned to. The restriction here is similar, except it has been generalised to be effectively final. Simply put, you can refer to variables that aren't being assigned to more than once. The principle is that a variable is effectively final if it is either marked as final, or could be marked as final without introducing a compiler error.

Trying and Links

Part of the Early Draft Review of the lambdas specification has been put online already, and if you want to know the ins and outs of what's happening this might make an interesting read for you. There are already binary snapshots if you're interested in trying to write some lambda code. These are updated every so often, and if you want to have a look at the guts of the lambda internals then you can try a source build. My only caveats about writing code using the binary is that it's still somewhat buggy, since it's at a draft release and also it's using the '#' operator instead of '::' as the method reference syntax.

Summary

This has been an introduction to some of the concepts of Java 8's Lambda expressions, consequently it simplifies some of the issues involved, but it should be enough for the reader to understand what's coming soon. It's also great to see Oracle introducing some useful changes into the language side of things, which has been stagnating for a while.