Navigation: Overview - Part A - Part B - Part C - Part D - Part E - Part F - Part G - Part H - Part J
Sections: 15.27 - 15.27.3 - 15.27.4 - 15.28 - 15.28.1 - 15.28.2 - 11.2.1 - 11.2.3 - 12.5
Version 0.6.2. Copyright © 2012 Oracle America, Inc. Legal Notice.
Lambda expressions and method references are always poly expressions. It is a compile-time error if one of these occurs in a program in someplace other than an assignment context, an invocation context, or a casting context.
The type of a lambda expression or method reference is its target type, assuming the expression is compatible with its target type; otherwise, a compile-time error occurs. Compatibility depends on a function descriptor; to derive this descriptor, a function type target is required.
For a lambda expression, the descriptor's parameter and return types are compared to the expression. The lambda parameter types must exactly match those of the descriptor, while the body must be assignment-compatible with the descriptor's return type. The lambda's expression body (or each result expression of its block body) may be a poly expression.
For a method reference, a compile-time declaration is determined following the process used for method invocations. The descriptor's parameter types are used as argument types in this search, where the first parameter type may sometimes act as the receiver for an instance method. The selected declaration's return type is then checked to be assignment-compatible with the descriptor's return type.
In addition to the compatibility requirement, lambda bodies and referenced methods must not throw exceptions that are incompatible with the function descriptor's throws
clause.
Evaluation of a lambda expression or method reference produces an instance of a functional interface. Evaluation does not cause the execution of a lambda body or the invocation of a referenced method; instead, this may occur at a later time when an appropriate method of the interface is invoked.
To evaluate the expression, either a new instance of an appropriate class is allocated and initialized, or an existing instance of an appropriate class is referenced. The evaluation rules are minimally restrictive, thus allowing VMs freedom for optimization. For example, a separate class need not be defined for each distinct expression, nor must a new object be allocated on every evaluation.
See 15.27
Lambda expressions are always poly expressions (15.2). [jsr335-15.27-10]
It is a compile-time error if a lambda expression occurs in a program in someplace other than an assignment context (5.2), an invocation context (5.3), or a casting context (5.5). [jsr335-15.27-20]
Evaluation of a lambda expression produces an instance of a functional interface (9.8). Lambda expression evaluation does not cause the execution of the expression's body; instead, this may occur at a later time when an appropriate method of the functional interface is invoked.
If a lambda expression is compatible with its target type, T, then the type of the expression is T. [jsr335-15.27.3-10]
A lambda expression is compatible in an assignment, invocation, or casting context with type T if T is a function type (9.8) and the expression is congruent with a function descriptor derived from T. [jsr335-15.27.3-15]
The targeted function descriptor is derived from the target type as follows: [jsr335-15.27.3-20]
<
A1, ..., Am>
, and if the lambda expression has explicit parameter types P1, ..., Pn, then the targeted function descriptor is the descriptor of the inferred wildcard instantiation of F<
A1, ..., Am>
for P1, ..., Pn, as defined in 18.5.3. [jsr335-15.27.3-20-D]
A lambda expression is congruent with a function descriptor if the following are true: [jsr335-15.27.3-30]
void
, then the lambda body is either a statement expression or a void
-compatible block. [jsr335-15.27.3-30-E]
void
) type R, then either i) the lambda body is an expression that is compatible with R in an assignment context, or ii) the lambda body is a value-compatible block, and each result expression is compatible with R in an assignment context. [jsr335-15.27.3-30-F]
A lambda expression may be illegal even if it has a type. Where T' is the type of the lambda expression:
Discussion and motivation:
There is some flexibility in the design of lambda expression compatibility: a well-formedness check may either occur as part of the definition, or as an extra check after compatibility is established. Since overload resolution depends on compatibility but not subsequent checks (see Part F), this distinction is important. In one extreme, every check could be part of compatibility—in that case, subtle differences in, say, the exceptions thrown by the lambda body, could trigger different overloading choices. In the other extreme, the expressions could always be "compatible," and then overload resolution would be unable to distinguish between methods with different descriptor argument types.
The approach we've chosen is to allow most errors in the lambda body to indicate incompatibility, except for exception checking. We don't rely on exceptions for two reasons:
- Exception checking is subtle, and the common practice is to refine
throws
clauses based on compiler feedback. We would not want this "feedback" to take the form of subtle overload resolution changes (by making the lambda compatible with different sets of target types depending on the body's exceptions).- Inference may influence the target type's
throws
clause and the exceptions thrown by return expressions in the body. It is easiest to cope with these dependencies by avoiding exception checking until after overload resolution and inference are complete.We require lambda parameter types to exactly match those of the functional interface descriptor. While it would be possible to be more flexible—allow boxing or contravariance, for example—this kind of generality seems unnecessary, and is inconsistent with the way overriding works in class declarations. A programmer ought to know exactly what function type is being targeted when writing a lambda expression, so he should thus know exactly what signature must be overridden. (In contrast, this is not the case for method references, and so more flexibility is allowed when they are used.) In addition, more flexibility with parameter types would add to the complexity of type inference and overload resolution.
While boxing is not allowed in a strict invocation context, boxing of lambda return expressions is always allowed—that is, the return expression appears in an assignment context, regardless of the context enclosing the lambda expression.
However, if a lambda expression is an argument to an overloaded method, a method signature that avoids boxing or unboxing the lambda result is preferred by the most-specific check (see 15.12.2.5).
Similarly, lambda returns are allowed to perform narrowing of constant expressions, as in all assignment contexts.
If the body of a lambda is a statement expression (that is, an expression that would be allowed to stand alone as a statement), it is compatible with a
void
-producing function descriptor; any result is simply discarded. So, for example, both of the following are legal:// Predicate has a boolean return Predicate<String> p = s -> list.add(s); // Block has a void return Block<String> b = s -> list.add(s);Generally speaking, a lambda of the form
() ->
expr, where expr is a statement expression, is interpreted as either() -> { return
expr; }
or() -> {
expr; }
, depending on the target type.
At run time, the evaluation of a lambda expression (distinct from execution of a lambda body) either produces a reference to an object of the targeted function type or completes abruptly. [jsr335-15.27.4-10]
The value of a lambda expression is a reference to an instance of a class with the following properties: [jsr335-15.27.4-20]
Object
class. [jsr335-15.27.4-20-E]
To evaluate the lambda expression, either a new instance of an appropriate class is allocated and initialized, or an existing instance of an appropriate class is referenced. [jsr335-15.27.4-30]
This implies that the behavior of an equality operator (15.21) is unpredictable when applied to the result of evaluation of a lambda expression: the equality test may produce different results in different implementations, or even upon different lambda expression evaluations in the same implementation.
If a new instance is to be created, but there is insufficient space to allocate the object, evaluation of the lambda expression completes abruptly by throwing an OutOfMemoryError
(15.9.6). [jsr335-15.27.4-40]
This section was influenced by 15.9.4 "Run-time Evaluation of Class Instance Creation Expressions."
Discussion and motivation:
This section is meant to be minimally restrictive, thus allowing VMs freedom for optimization. Some flexibility it provides:
- A new object need not be allocated on every evaluation
- Objects produced by different lambda expressions need not belong to different classes (if the bodies are identical, for example)
- Every object produced by evaluation need not belong to the same class (captured local variables might be inlined, for example)
- If an "existing instance" is available, it need not have been created at a previous lambda evaluation (it might have been allocated during the enclosing class's initialization, for example).
While it is outside the scope of the language specification to prescribe compiler behavior, it is expected that all compilers will encode lambda bodies as methods, typically of the enclosing class. The evaluation behavior specified above will then be achieved via aninvokedynamic
instruction that mentions the compiled method and invokes a standard API. The runtime library, independent of the compiler, provides the implementation of this API and is free to use whatever strategy it prefers to manage class and object creation. If the targeted function type is a subtype ofSerializable
, the resulting object will automatically be an instance of a serializable class. Making an object derived from a lambda expression serializable can have extra runtime overhead and negative security implications, so we do not require all lambda-derived objects to be serializable.The standard API for lambda evaluation provides special support for serialization which is more robust than the automatic graph-traversal behavior, and which is supported across different VM implementations.
It is expected that the lambda implementation class will typically override thetoString
method, since the implementation provided byObject
is quite unhelpful—any useful information about the lambda expression, if theObject
implementation were used, could only be deduced from the class's name.
See 15.28
Method reference expressions are always poly expressions (15.2). [jsr335-15.28-10]
It is a compile-time error if a method reference expression occurs in a program in someplace other than an assignment context (5.2), an invocation context (5.3), or a casting context (5.5). [jsr335-15.28-20]
Evaluation of a method reference expression produces an instance of a functional interface (9.8). Method reference evaluation does not cause the execution of the corresponding method; instead, this may occur at a later time when an appropriate method of the functional interface is invoked.
If a method reference expression is compatible with its target type, T, then the type of the expression is T. [jsr335-15.28.1-10]
A method reference expression is compatible in an assignment, invocation, or casting context with type T if T is a function type (9.8) and the expression is congruent with the function descriptor of T. [jsr335-15.28.1-15]
A method reference is congruent with a function descriptor if the following are true: [jsr335-15.28.1-30]
void
. [jsr335-15.28.1-30-B1]
void
; and R' is compatible with R in an assignment context. [jsr335-15.28.1-30-B2]
The compile-time declaration of a method reference is either a declared method or, in special cases, a notional method that represents a class instance creation or an array creation. Given a function descriptor with parameter types P1..Pn, the compile-time declaration for a method reference is determined as follows. [jsr335-15.28.1-40]
::
NonWildTypeArgumentsopt Identifier, then two searches for a most-specific applicable method are performed, following the process outlined in 15.12.2. Each search may produce a method or, in the case of an error as specified in 15.12.2, no result. [jsr335-15.28.1-45]First, the reference is treated as if it were an invocation of a method named by Identifier with argument expressions of types P1..Pn; the type to search is the ReferenceType; the type arguments, if any, are given by the method reference. [jsr335-15.28.1-45-A]
Second, if P1..Pn is not empty and P1 is a subtype of ReferenceType, the reference is treated as if it were an invocation of a method named by Identifier with argument expressions of types P2..Pn. If the ReferenceType is a raw type, and there exists a parameterization of this type, G, that is a supertype of P1, the type to search is G; otherwise, the type to search is the ReferenceType. Again, the type arguments, if any, are given by the method reference. [jsr335-15.28.1-45-B]
If both of these searches match a method, the reference is considered ambiguous. [jsr335-15.28.1-45-C]
Otherwise, if one of the searches matches a method, that is the compile-time declaration. [jsr335-15.28.1-45-E]
::
NonWildTypeArgumentsopt new
, a set of notional methods corresponding to constructors of the named class is searched, following the process outlined in 15.12.2; the invocation's argument types are P1..Pn, and the type arguments (if any) are given by the method reference. [jsr335-15.28.1-52] If ClassType is a raw type, the methods to search are as described by 15.9.3 for a class instance creation that uses <>
to elide type arguments. Otherwise, the methods to search are derived directly from the constructors of ClassType; the return type of each is ClassType. [jsr335-15.28.1-52-A]
If this search would result in an error, as specified in 15.12.2, there is no compile-time declaration. Otherwise, the compile-time declaration is the most-specific applicable notional method. [jsr335-15.28.1-52-B]
::
new
, a single notional method is tested for applicability, following the process outlined in 15.12.2; the invocation's argument types are P1..Pn. The notional method has a single parameter of type int
, returns the ArrayType, and has no throws
clause. If the test would result in an error, as specified in 15.12.2, there is no compile-time declaration. Otherwise, the method is applicable and it is the compile-time declaration. [jsr335-15.28.1-53]
A method reference expression may be illegal even if it is compatible with its expected type. In particular, where T' is the type of the method reference:
throws
clause of the type of the compile-time declaration, a compile-time error occurs unless that exception type or a supertype of that exception type is mentioned in the throws
clause of the descriptor of T'. [jsr335-15.28.1-60]
static
, unless the method reference is of the form ReferenceType ::
NonWildTypeArgumentsopt Identifier, the ReferenceType is not a parameterized type, and the compile-time declaration was identified via a search with argument expressions of types P1..Pn. [jsr335-15.28.1-80]
::
NonWildTypeArgumentsopt Identifier and the compile-time declaration is an instance method, unless the compile-time declaration was identified via a search with argument expressions of types P2..Pn. [jsr335-15.28.1-85]
super
::
NonWildTypeArgumentsopt Identifier and the compile-time declaration is abstract
. [jsr335-15.28.1-90]
.
super
::
NonWildTypeArgumentsopt Identifier, and either of the following are true: [jsr335-15.28.1-91]
abstract
. [jsr335-15.28.1-91-A]
::
NonWildTypeArgumentsopt new
and an error would occur when determining an enclosing instance for a ClassType, as described in 15.9.2 (treating the method reference as if it were a class instance creation expression). [jsr335-15.28.1-95]
Parts of this section mimic 15.27.3.
Discussion and motivation:
The key idea driving this compatibility definition is that a method reference is compatible if and only if the equivalent lambda expression(x, y, z) -> exp.<T1, T2>method(x, y, z)
is compatible. (This is informal, and there are issues that make it difficult or impossible to formally define the semantics in terms of such a rewrite.)Method references of the form
ReferenceType::id
can be interpreted in different ways. It is ambiguous whether the identifier refers to a static method or an instance method; in the latter case, the implicit lambda expression has an extra parameter. It's possible, of course, for both kinds of applicable methods to exist, and the search for an applicable method via 15.12.2 must identify them separately, since there are different parameter types for each case.Also note that static method invocations have the form
TypeName.method()
, while here we're using the more general ReferenceType; an error occurs if the reference ends up being a static method reference and the qualifier is a parameterized type.An example of ambiguity:
interface Fun<T,R> { R apply(T arg); } class C { int size() { return 3; } static int size(C arg) { return arg.size(); } void test() { Fun<C, Integer> f1 = C::size; // c.size() or C.size(c)? } } These compatibility rules provide a convenient facility for converting from one functional interface to another:Task t = () -> System.out.println("hi"); Runnable r = t::invoke;The implementation may be optimized so that when a lambda-derived object is passed around and converted to various types, this does not result in many levels of adaptation logic around the core lambda body.
For convenience, when a generic type is used to refer to an instance method (where the receiver becomes the first parameter), the target type can used to determine the type arguments. This facilitates usage like
Pair::first
in place ofPair<String,Integer>::first
.Similarly, a method reference like
Pair::new
is treated like a "diamond" instance creation (new Pair<>()
). There is no need for the<>
syntax here; in fact, it is not allowed by the grammar. Note that this form does not instantiate a raw type, and there is no way to express a reference to a raw type constructor—this is because it seems that the raw instance creation would almost never be more useful than its inferred-parameters counterpart.
At run time, the evaluation of a method reference (distinct from invocation of the method itself) either produces a reference to an object of the targeted function type or completes abruptly. [jsr335-15.28.2-10]
The value of a method reference is a reference to an instance of a class with the following properties: [jsr335-15.28.2-20]
void
, then the body returns the result of the method invocation or object instantiation, after any necessary assignment conversions (5.2). [jsr335-15.28.2-20-E]
Object
class. [jsr335-15.28.2-20-F]
If the method reference has the form ExpressionName ::
NonWildTypeArgumentsopt Identifier or Primary ::
NonWildTypeArgumentsopt Identifier, the body of the invocation method has the effect of invoking the compile-time declaration of the method reference, as described in 15.12.4.3, 15.12.4.4, 15.12.4.5. [jsr335-15.28.2-41]
If the method reference has the form super
::
NonWildTypeArgumentsopt Identifier or TypeName .
super
::
NonWildTypeArgumentsopt Identifier, the body of the invocation method similarly has the effect of invoking the compile-time declaration of the method reference, as described in 15.12.4.3, 15.12.4.4, 15.12.4.5. [jsr335-15.28.2-42]
super
. [jsr335-15.28.2-42-A]
.
this
at the point at which the method reference is evaluated. Otherwise, the target reference is the value of this
at the point at which the method reference is evaluated. [jsr335-15.28.2-42-B]
If the method reference has the form ReferenceType ::
NonWildTypeArgumentsopt Identifier, the body of the invocation method similarly has the effect of invoking the compile-time declaration of the method reference, as described in 15.12.4.3, 15.12.4.4, 15.12.4.5. [jsr335-15.28.2-43]
If the method reference has the form ClassType ::
NonWildTypeArgumentsopt new
, the body of the invocation method has the same effect as a class instance creation expression of the form new
NonWildTypeArgumentsopt ClassType(param1, ..., paramn) (15.9.4), where param1, ..., paramn are the parameters of the invocation method. [jsr335-15.28.2-50]
If the method reference has the form ArrayType ::
new
, the body of the invocation method has the same effect as an array creation expression of the form new
ArrayType [
size ]
, where size is the invocation method's single parameter. [jsr335-15.28.2-52]
To evaluate the method reference, the following steps are taken: [jsr335-15.28.2-30]
If the subexpression evaluation completes abruptly, then the method reference evaluation completes abruptly for the same reason. [jsr335-15.28.2-30-A1]
If the result of evaluation of the subexpression is null
, then a NullPointerException
is thrown. [jsr335-15.28.2-30-A2]
This implies that the behavior of an equality operator (15.21) is unpredictable when applied to the result of evaluation of a method reference: the equality test may produce different results in different implementations, or even upon different method reference evaluations in the same implementation.
If a new instance is to be created, but there is insufficient space to allocate the object, evaluation of the method reference completes abruptly by throwing an OutOfMemoryError
(15.9.6). [jsr335-15.28.2-30-B1]
This section borrows heavily 15.27.4, above. The rules for invocation also reproduce, in simplified form, some of 15.12.4.
Discussion and motivation:
The timing of method reference evaluation is a little more complex than that of lambda expressions: when a method reference is qualified with an expression (rather than a type), the expression is evaluated immediately. The result of evaluation is then stored until the functional interface's method is invoked; at that point, the value is used as the target reference for the invocation. This means that the portion of the method reference preceding the::
delimiter is only evaluated when the program encounters the method reference; subsequent invocations of the functional interface do not re-evaluate the subexpression. When a method invocation is evaluated, it is possible for the Primary that qualifies the invocation to producenull
, but for noNullPointerException
to occur. This occurs when the invoked method isstatic
(despite the syntax of the invocation suggesting an instance method). Since we explicitly prohibit a matched method for this kind of method reference from beingstatic
(15.28.1), the evaluation behavior described here is simpler—anull
primary always triggers aNullPointerException
. As was the case for lambda expression evaluation, it is outside the scope of the language specification to prescribe compiler behavior, but it is expected that all compilers will generate aninvokevirtual
instruction that delegates to a standard API the wrapping of a referenced method as an instance of a particular interface.
Compare JLS 11.2.1
A class instance creation expression (15.9) can throw an exception class E iff ... [jls-11.2.1-100]
A method invocation expression (15.12) can throw an exception class E iff ... [jls-11.2.1-110]
A lambda expression (15.27) can throw no exception types. [jsr335-11.2.1-120]
For every other kind of expression, the expression can throw an exception type E iff one of its immediate subexpressions can throw E. [jls-11.2.1-200]
Compare JLS 11.2.3
It is a compile-time error if a method or constructor body can throw some exception class E when E is a checked exception class and E is not a subclass of some class declared in the throws
clause of the method or constructor. [jls-11.2.3-100]
It is a compile-time error if a lambda body can throw some exception class E when E is a checked exception class and E is not a subclass of some class declared in the throws
clause of the function descriptor targeted by the lambda expression. [jsr335-11.2.3-105]
It is a compile-time error if a class variable initializer (8.3.2) or static initializer (8.7) of a named class or interface can throw a checked exception class. [jls-11.2.3-110]
It is a compile-time error if an instance variable initializer or instance initializer of a named class can throw a checked exception class unless that exception class or one of its superclasses is explicitly declared in the throws
clause of each constructor of its class and the class has at least one explicitly declared constructor. [jls-11.2.3-120]
...
It is a compile-time error if a catch
clause can catch checked exception class E1 and it is not the case that the try
block corresponding to the catch
clause can throw a checked exception class that is a subclass or superclass of E1, unless E1 is Exception
or a superclass of Exception
. [jls-11.2.3-200]
It is a compile-time error if a catch
clause can catch (11.2) checked exception class E1 and a preceding catch
clause of the immediately enclosing try
statement can catch E1 or a superclass of E1. [jls-11.2.3-210]
...
Compare JLS 12.5
A new class instance may be implicitly created in the following situations: