Introduction to Java LambdasOctober 26th, 2013
The main theme of Java 8 is lambdas. I have noticed that for many Java programmers lambdas are pretty tough material. So let’s try to get a basic understanding of them.
First of all, what exactly is a lambda? A lambda is an anonymous function that is, unlike a regular function, not bound to an identifier (i.e. it has no name). These functions can be passed as arguments to other functions (known as higher-order functions).
Suppose our application must write to a bunch of files from different places of the system. We don’t want to handle the checked exceptions every time [see Exceptions: checked and unchecked for more about checked exceptions]. So we decide to write a low-level
writeToFile function that opens a
FileWriter and passes it to a function that can then safely write to the file.
Using this low-level function we write the following code.
The object we pass into
writeToFile is an anonymous implementation of
FileWriteFunction [anonymous because we didn’t name it as a class]. It has a single function making this effectively passing an anonymous function. In the world of Java these are sometimes referred to as callbacks. Probably you’ve used this at least a few times before, maybe without taking notice.
This anonymous object is effectively a lambda. But obviously this doesn’t look like passing a function. The syntax is very unwieldy. And this is exactly what changes in Java 8.
With the syntactic support for lambdas in Java 8 the code reads like we pass a function. Using a Java 8 lambda we rewrite the above code as follows.
That’s better. It emphasizes the code that matters and hides most of the unwieldy parts.
Often lambdas are used interchangeably with lexical functions (closures). While they are both anonymous functions, the definition of a closure is that it is a function containing bound variables. I.e. the closure includes a referencing table that contains references to local variables.
For example, if we accept a parameter
data that we want to write to a file, we use a closure.
While anonymous inner-classes restrict access to final variables, closures provide access to any variable. However, the variable is effectively final to the closure, so it cannot be reassigned.
What about compilation of lambdas? Does Java 8 only provide a spoonful of syntactic sugars upon anonymous inner-classes with only one method?
Not really, no. It’s true that it allows the lambda syntax for any single-method anonymous inner-class. But lambdas are not compiled into inner-classes. Instead the compiler outputs
lambda$ methods in the defining class and uses
invokedynamic to dispatch the call.
So now you know how to use a lambda in Java 8. While by themselves lambdas are pretty useful, they are even more so when applying them to collections.
The new Stream API provides an alternative to iterators by offering a more functional API to collections:
java.util.stream.Stream. The most noteworthy functions on a
To start with a simple example, here’s how to sum all the numbers in a list.
This reduces the sequence by adding each value to an accumulator, starting at zero. For comparison, classically you’d write a loop.
Moving on to sum only odd numbers. First we
filter odd numbers, then we
The argument to
filter is a function reference to a static
odd function in a
Predicates class I used. It is a boolean function that, as the name suggests, tests if a number is odd.
So far, so good. Now suppose we want to transform a list of centimeter sizes to their equivalent size in inches. We use
map for this.
The centimeters are mapped to inches by applying the
toInches function to every item in the
By its nature,
Stream is continuous. It is used to describe the operations to apply on a collection. But to acquire the results the data must be collected. This is what the
collect function is for. It reduces the elements of the stream to a mutable container [e.g. a list].
Using the Stream API and lambdas can greatly simplify the code you use to work with collections, and make the code much more expressive. Preferring using non-destructive operations (e.g.
filter) over destructive operations (e.g.
forEach) makes the code more easy to reason about.
That’s it! These are the basics you need to know about lambdas (and closures) in Java 8. Of course there’s much more to write about lambdas, but that’s something for another post.