13.2 Lambda Expressions
Lambda expressions implement functional interfaces, and thereby define anonymous functions—that is, functions that do not have a name. They can be used as a value in a program, without the excess baggage of first being packaged into objects. As we shall see, these two features together facilitate behavior parameterization— that is, customizing method behavior by passing code as values.
A lambda expression has the following syntax:
formal_parameter_list -> lambda_body
The parameter list and the body are separated by the -> operator (a.k.a. the function arrow). The arrow operator has the penultimate level of precedence, only higher than the assignment operators which have the lowest precedence, and is right-associative.
The lambda expression syntax resembles a simplified declaration of a method, without many of the bells and whistles of a method declaration—omitting, in particular, any modifiers, return type, or throws clause specification. That is important, as it avoids verbosity and provides a simple and succinct notation to write lambda expressions on the fly.
A lambda expression can only occur in specific contexts: for example, as the value on the right-hand side of an assignment, as an argument passed in a method or constructor call, or as the value to cast with the cast operator (p. 733). Such an occurrence defines a lambda expression, which is evaluated at runtime to produce a new kind of value: an instance of a functional interface. The (deferred) execution of the lambda body only occurs at a later time when the sole abstract method of this functional interface instance is invoked.
In the rest of this section, we take a closer look at the parameter list, the lambda body, the type checking, and evaluation of lambda expressions.
Lambda Parameters
The parameter list of a lambda expression is a comma-separated list of formal parameters that is enclosed in parentheses, (), analogous to the parameter list in a method declaration. There is one special case where the parentheses can be omitted, which we shall get to shortly. The lambda body in the code examples in this subsection is intentionally kept simple—an empty code block, {}, that does nothing.
The formal parameters of a lambda expression can be declared as one of the following forms for parameter declarations:
- Declared-type parameters
If the types of the parameters are explicitly specified, they are known as declared-type parameters. The syntax of declared-type parameters is analogous to the formal parameters in a method declaration.
(Integer a, String y) -> {}; // Multiple declared-type parameters
- Inferred-type parameters
If the types of the parameters are not explicitly specified, they are known as inferred-type parameters. Types of the inferred-type parameters are derived from the context, and if necessary, from the functional type that is the target type of the lambda expression, as explained later in this section.
(a, b) -> {}; // Multiple inferred-type parameters
If the types of the parameters are explicitly specified with the reserved type name var, their type is inferred by local variable type inference (§3.13, p. 142). Thus the syntax of such a parameter is consistent with the syntax of a local variable declaration. We will refer to such parameters as var-type inferred parameters.
(var a, var b) -> {}; // Multiple var-type inferred parameters
A lambda expression with inferred parameters is called an implicitly typed lambda expression.
All parameters in the parameter list must conform to the same form of parameter declaration—mixing of different forms of parameter declaration is not allowed. Parentheses are mandatory with multiple parameters, whether they are declared-type or inferred-type. For a parameter list with a single inferred-type parameter, the parentheses can be omitted—but not for a declared-type or a var-type inferred parameter. Also, only declared-type parameters and var-type inferred parameters can have modifiers or annotations, like the final modifier or an annotation.
The code examples below illustrate the different forms of formal parameter declarations in a lambda expression.
() -> {}; // Empty parameter list
// Single formal parameter:
(String str) -> {}; // Single declared-type parameter
(str) -> {}; // Single inferred-type parameter
str -> {}; // Single inferred-type parameter
(var str) -> {}; // Single var-type inferred parameter
// Multiple formal parameters:
(Integer x, Integer y) -> {}; // Multiple declared-type parameters
(x, y) -> {}; // Multiple inferred-type parameters
(var x, var y) -> {}; // Multiple var-type inferred parameters
// Modifiers and annotations with formal parameters:
(final int i, int j) -> {}; // Modifier with declared-type parameters
(final var i, var j) -> {}; // Modifier with var-type inferred parameters
(@NonNull int i, int j) -> {}; // Annotation with declared-type parameter
(var i, @Nullable var j)-> {}; // Annotation with var-type inferred parameter
// Parentheses are mandatory with multiple formal parameters:
String str -> {}; // Illegal: Missing parentheses
var str -> {}; // Illegal: Missing parentheses
Integer x, Integer y -> {}; // Illegal: Missing parentheses
x, y -> {}; // Illegal: Missing parentheses
var x, var y -> {}; // Illegal: Missing parentheses
// All formal parameters must be either declared-type, inferred-type, or
// var-type inferred parameters.
(String str, j) -> {}; // Cannot mix declared-type and inferred-type
(String str, var j) -> {}; // Cannot mix declared-type and var-type inferred
(var str, j) -> {}; // Cannot mix var-type inferred and inferred-type
// Modifiers and annotations cannot be used with inferred-type parameters.
(final str, j) -> {}; // No modifiers with inferred-type parameters
(str, @NonNull j) -> {}; // No annotations with inferred-type parameters