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.
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
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.
Use a predicate to filter a collection: Here we have two new methods:
Iterable<T> filter(Predicate<? super T>)which retains only those elements for which the predicate holds true
<A extends Fillable<? super T>> A into(A)which fills the
ArrayListwith all the elements retained after filtering
We can also replace the
for loop with another new
void forEach(Block<? super T>):
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()).
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.
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.
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
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.
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.