Java was designed over 20 years ago as an
object-oriented
language.
- Advantage: Modularity and encapsulation
Long before object orientation, there were
functional
languages like Lisp and Scheme.
- Advantage: Well suited for concurrent and event-driven
programming
Java 8 attempts to blend object-oriented and functional programming.
The main concept Java 8 borrows from functional programming is
the
lambda expression:
- A block of code that can be passed around and
executed later, once or multiple times
- Not an object, best understood as
an anonymous function
Java already had the ability to pass around blocks of code as method
bodies.
For example, consider
the
Comparator interface. Here is a class
that implements it in order to compare strings by length.
It is followed by some code that uses an instance of the class:
The instance of
LengthComparator is used solely to pass some code to
the
Arrays.sort method.
Java has a feature by which classes that implement interfaces do not have
to be named.
This feature, called
anonymous classing, is useful when just a
single instance of a class is needed, as in our example
using
LengthComparator.
In the example below, no definition of the
LengthComparator
class is needed. Instead, the syntax produces an instance of an
anonynmous class that implements the
Comparator interface:
Lambda expressions take the idea of anonymous classes and extend it to
individual methods.
Consider the code in the
Comparator object that is used by
the
Arrays.sort method:
If an object were to execute this code, it would have to be given
first and
second as variables, and it would have to know
their types.
We can specify these types like this:
This is a legal "lambda expression" in Java: a block of code along with
the specification of any variables the code must use.
- The "lambda" terminology descends from logician Alonzo Church, who used the
Greek letter "λ" to indicate parameters in mathematical
functions.
- When a lambda expression's code is comprised of a single expression (as
in this example), the return value of the lambda is the return
value of the expression.
Lambda expressions are not objects,
and should be thought of as
anonymous functions.
If the code of a lambda does not fit in a single expression, it must be
written just like a method:
- Braces {...} must surround the code
- Explicit return statements must be given:
If the code of a lambda does not require parameters, empty parentheses
are required, as with parameterless methods:
Note that this code is like the
run method from
the
Runnable interface: it takes no parameters and returns no
values.
If the lambda's parameter types can be inferred, they can be omitted:
The parameter types can be inferred here because
comp is
a
String comparator.
If the lambda's code has one parameter whose type can be inferred, the
parentheses and the type can be omitted:
Since the listener is for an
ActionEvent, we know that it will
be passed an
ActionEvent object.
(Note that the
ActionEvent type in this context is for JavaFX.)
Even before Java 8, there were many existing interfaces that consisted
of a single method, for example:
Interface |
Method |
Runnable |
run |
Comparator |
compare |
ActionListener |
actionPerformed |
An object that is the instance of a class with one method can have only
one function—to execute that method—and it returns whatever that
method returns.
An interface with a single abstract method is therefore called
a
functional interface. Note:
- Default interface methods, because they have implementations, are not
abstract
- Static interface methods are also not abstract
Similarly, a lambda expression has only one function—to execute
its code—and it returns whatever its code returns.
Whenever an object of a class implementing a functional interface is
expected, you can supply a lambda expression instead.
Consider this use of a lambda expression to sort strings
(alphabetically, not by length) regardless of
letter case:
Since the work done by the lambda expression is exactly the action of a
single method call, the lambda expression can be replaced with
a "
method reference":
The "
::" operator separates the method name from the name of an
object or class.
Like lambda expressions, method references are turned into instances of
functional interfaces.
Here is another use of a lambda expression where an object implementing
a functional interface is expected:
Calling
repeatMessage("Hello", 100) will print "Hello" 100 times
in a separate thread.
Q: How does the lambda's code access the
variables
count and
text, which occur "free" in the
lambda?
Here is a full description of what a lambda expression is:
- A block of code
- Parameters
- Values for the code's free variables, that is, the variables
that are not parameters and not defined inside the code
The combination of code and the values of the free
variables occurring in it is called a
closure.
Lambda expressions can safely access their free variables only if their
values do not change after they have been "captured" in the lambda.
Before Java 8, you had to declare a variable "
final" to capture it
in inner, anonymous classes.
In Java 8, a variable can be "
effectively final" even if it is
not declared to be
final so long as the compiler cannot detect
that it has been changed.
Below is an example of a lambda expression using a free variable that is
not effectively final. It will not compile: