About Bart Bakker

Bart is a technologist who specializes in agile software development. He is passionate about creating working software that is easy to change and to maintain.

Introduction to Java lambdas

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.

writeToFile("todo.txt", new FileWriteFunction() {
    @Override
    public void apply(Writer file) throws IOException {
        file.write("learn about lambdas\n");
        file.write("learn stream API\n");
    }
})

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.

writeToFile("todo.txt", file -> {
    file.write("learn about lambdas\n");
    file.write("learn stream API\n");
})

That’s better. It emphasizes the code that matters and hides most of the unwieldy parts.

Often lambdas are used interchangeably with closures (i.e. lexical functions). 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.

void save(String data) {
    writeToFile("file.db", file -> file.write(data) );
}

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 Stream are: collect, filter, map, and reduce.

To start with a simple example, here’s how to sum all the numbers in a list.

asList(1,2,3,4,5).stream()
    .reduce(0, (acc, value) -> acc + value) // => 15

This reduces the sequence by adding each value to an accumulator, starting at zero. For comparison, classically you’d write a loop.

int acc = 0;
for (int n : asList(1,2,3,4,5))
    acc += n;
acc // => 15

Moving on to sum only odd numbers. First we filter odd numbers, then we reduce.

asList(1,2,3,4,5).stream()
    .filter(Predicates::odd)
    .reduce(0, (acc, n) -> acc + n) // => 9

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.

List<Inch> inches = centimeters.stream()
    .map(Centimeter::toInches)
    .collect(Collectors.toList())

The centimeters are mapped to inches by applying the toInches function to every item in the centimeters collection.

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. map, 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.
 

Reference: Introduction to Java lambdas from our JCG partner Bart Bakker at the Software Craft blog.
Related Whitepaper:

Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions

Get ready to program in a whole new way!

Functional Programming in Java will help you quickly get on top of the new, essential Java 8 language features and the functional style that will change and improve your code. This short, targeted book will help you make the paradigm shift from the old imperative way to a less error-prone, more elegant, and concise coding style that’s also a breeze to parallelize. You'll explore the syntax and semantics of lambda expressions, method and constructor references, and functional interfaces. You'll design and write applications better using the new standards in Java 8 and the JDK.

Get it Now!  

Leave a Reply


− seven = 0



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close