DatumEdge The website of James Shaw

Java 8 lambdas

23 June 2012

Scheduled for release in 2013, Java 8 will include language support for lambda functions. Although the specification is still in flux, lambdas are already implemented in JDK 8 binaries.

This article takes a tour of the new lambda syntax, the use of lambdas in the Collections API, and related language enhancements. All code snippets were compiled with JDK 8 lambda build b39.

Functional interfaces

Interfaces that have just one method are called functional interfaces. Lambda expressions can be used anywhere we have a functional interface.

java.awt.event.ActionListener is a functional interface since it has one method, void actionPerformed(ActionEvent). In Java 7 we can write In Java 8 this becomes

Here, the compiler knows that the lambda expression must conform to the signature void actionPerformed(ActionEvent). It sees that the lambda body returns void, and it can infer that type of parameter e is java.awt.event.ActionEvent.

Functional collections

The Java 8 class library has a new package, java.util.functions, which contains several new functional interfaces. Many of these can be used with the Collections API.

java.util.functions.Predicate

Use a predicate to filter a collection: Here we have two new methods:

java.util.functions.Block

We can also replace the for loop with another new Iterable method, void forEach(Block<? super T>): The forEach() method is an example of internal iteration: iteration happens inside the Iterable and our Block can see only one element at a time.

Finally, a less trivial example of functional programming with the Collections API:

Programming collections in this style has some advantages:

Elements may be computed lazily
If we apply a Mapper to a collection of a thousand elements but only iterate over the first three, the remaining elements will never be mapped.
Method chaining is encouraged
Hence there's no need to store intermediate results in their own collections.
Internal iteration hides implementation decisions
For example, we could parallelize a map() operation just by writing myCollection.parallel().map(e ‑> e.length()).

Method references

We can reference a method using the :: syntax. Method references are treated the same way as lambda expressions and can be used wherever a functional interface is accepted.

We can reference a static method: Or an instance method: We can also create factories by assigning a constructor reference to java.util.functions.Factory:

Lastly, let's create a reference to a method of an arbitrary instance: Here we do not need to bind the method reference to an instance. Instead, we pass the instance (in this case, address) as the first argument to the functional interface.

Default methods

With Java today, it is not possible to add methods to a published interface without breaking existing implementations. Java 8 gives us a way to specify a default implementation in the interface itself: Subinterfaces can override a default method: Or a subinterface can remove the default by redeclaring the method without a body: Doing this forces an implementation of FastQueue to implement deleteAll().

HotSpot implementation

Not only do lambda expressions give rise to more compact source code, but their bytecode and runtime implementation can be more efficient than the anonymous classes we would see in Java 7. For each lambda expression it finds, the compiler creates a method such as lambda$1(). This process is called lambda body desugaring. When the lambda expression is captured, the compiler emits an invokedynamic call site. This call site is responsible for capturing values from the lexical scope and binding the desugared method to the target functional interface.

Further reading

Much of this post is based on Brian Goetz's articles State of the Lambda, State of the Lambda: Libraries Edition and Translation of Lambda Expressions. Between them, these articles address the details of lambda syntax, variable capture, type inference and compilation.