Lambda Specification, Part B: Lambda Expressions

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.1 - 15.27.2 - 3.9 - 3.12 - 4.12.3 - 4.12.4 - 6.3 - 6.4 - 8.1.3 - 14.15 - 14.16 - 14.17 - 14.18 - 14.20 - 15.8.3 - 16
Version 0.6.2. Copyright © 2012 Oracle America, Inc. Legal Notice.

Summary

A lambda expression is like a method: it provides a list of formal parameters and a body—an expression or block—expressed in terms of those parameters.

Examples of lambda expressions:

s -> s.length()

(int x, int y) -> x+y

() -> 42

(x, y, z) -> {
  if (true) return x;
  else {
    int result = y;
    for (int i = 1; i < z; i++)
      result *= i;
    return result;
  }
}

This particular syntax was chosen principally because something similar has been generally well-received in other Java-like languages (C# and Scala), and a clearly "better" alternative did not present itself. It has the advantage over alternate proposals of minimizing bracketing noise around simple lambda expressions. When some extra bracketing is needed to visually distinguish either the full lambda expression or its body expression, parentheses are naturally supported (just as in other cases in which operator precedence is unclear).

The formal parameters of a lambda expression may have either declared types or inferred types. If the formal parameters have inferred types, then these types are derived from the function type targeted by the lambda expression.

A lambda body is either a single expression or a block. Like a method body, a lambda body describes code that will be executed whenever an invocation occurs.

Unlike code appearing in anonymous class declarations, the meaning of names and the this and super keywords appearing in a lambda body are the same as in the surrounding context (except that lambda parameters introduce new names).

For both lambda bodies and inner classes, local variables in the enclosing context can only be referenced if they are final or effectively final. A variable is effectively final if it is never assigned to after its initialization.

Block lambda bodies that don't return anything are considered void-compatible; block lambda bodies that return a value from every execution path are considered value-compatible. In a value-compatible lambda body, the result expressions are any expressions that may produce an invocation's value.

15.27 Lambda Expressions [New]

A lambda expression is like a method: it provides a list of formal parameters and a body—an expression or block—expressed in terms of those parameters.

LambdaExpression:
  LambdaParameters '->' LambdaBody

LambdaParameters:
  Identifier
  '(' FormalParameterListopt ')'
  '(' InferredFormalParameterList ')'

InferredFormalParameterList:
  Identifier
  InferredFormalParameterList ',' Identifier

LambdaBody:
  Expression
  Block

The following definitions from 8.4.1 are repeated here for convenience:

FormalParameterList:
  LastFormalParameter
  FormalParameters ',' LastFormalParameter

FormalParameters:
  FormalParameter
  FormalParameters, FormalParameter

FormalParameter:
  VariableModifiersopt Type VariableDeclaratorId

LastFormalParameter:
  VariableModifiersopt Type '...' VariableDeclaratorId
  FormalParameter
Examples of lambda expressions:
() -> {}                     // No parameters; result is void

() -> 42                     // No parameters, expression body
() -> null                   // No parameters, expression body
() -> { return 42; }         // No parameters, block body with return
() -> { System.gc(); }       // No parameters, void block body

() -> {
  if (true) return 12;
  else {
    int result = 15;
    for (int i = 1; i < 10; i++)
      result *= i;
    return result;
  }
}                          // Complex block body with returns

(int x) -> x+1             // Single declared-type parameter
(int x) -> { return x+1; } // Single declared-type parameter
(x) -> x+1                 // Single inferred-type parameter
x -> x+1                   // Parens optional for single inferred-type case

(String s) -> s.length()   // Single declared-type parameter
(Thread t) -> { t.start(); } // Single declared-type parameter
s -> s.length()              // Single inferred-type parameter
t -> { t.start(); }          // Single inferred-type parameter

(int x, int y) -> x+y      // Multiple declared-type parameters
(x,y) -> x+y               // Multiple inferred-type parameters
(final int x) -> x+1       // Modified declared-type parameter
(x, final y) -> x+y        // Illegal: can't modify inferred-type parameters
(x, int y) -> x+y          // Illegal: can't mix inferred and declared types
Discussion and motivation:
  1. This particular syntax was chosen principally because something similar has been generally well-received in other Java-like languages (C# and Scala), and a clearly "better" alternative did not present itself. It has the advantage over alternate proposals of minimizing bracketing noise around simple lambda expressions; this is especially noticeable when a lambda expression is an argument to a method, or when the body is another lambda expression. It also clearly distinguishes between its expression and statement forms, which avoids ambiguities or over-reliance on ';' tokens.

    When some extra bracketing is needed to visually distinguish either the full lambda expression or its body expression, parentheses are naturally supported (just as in other cases in which operator precedence is unclear).

    There is no special nullary form: a lambda expression with 0 arguments is expressed as () -> .... The obvious special-case syntax, -> ..., does not work because it introduces an ambiguity between argument lists and casts: (x) -> ....

    A nice property of this syntax for block bodies is that established conventions for indenting and line-breaking other block-like syntax extend quite naturally to lambda expressions.

  2. This syntax introduces some new parsing challenges, although they are similar in scope to what is already handled by the Java grammar.

    Java has always had an ambiguity between types and expressions after a '(' token (what follows may be a cast or a parenthesized expression). This was made worse in Java 5, which reused the binary operators '<' and '>' in types.

    Lambda expressions introduce a new possibility: the tokens following '(' may describe a type, an expression, or a lambda parameter list. Some tokens (annotations, final) are unique to parameter lists, while in other cases there are certain patterns that must be interpreted as parameter lists (two names in a row, a ',' not nested inside of '<' and '>'). And sometimes the ambiguity cannot be resolved until a '->' is encountered, after a ')'. The simplest way to think of how this might be efficiently parsed is with a state machine: each state represents a subset of possible interpretations (type, expression, or parameters), and when the machine transitions to a state in which the set is a singleton, the parser knows which case it is. This does not map very elegantly to a fixed-lookahead grammar, however.

  3. Lambda expressions cannot introduce type parameters. While it would make sense semantically to do so, the natural syntax (preceding the parameter list with a type parameter list) introduces messy ambiguities. For example:
    foo( (x) < y , z > (w) -> v )

    This could be an invocation of foo with one argument (a generic lambda cast to type x), or it could be an invocation of foo with two arguments, both the results of comparisons, the second comparing z with a lambda expression. (Strictly speaking, a lambda expression is meaningless as an operand to a binary '>', but that is a tenuous assumption on which to build the grammar.)

    There is a precedent for ambiguity resolution involving casts, which essentially prohibits the use of - and + following a non-primitive cast, but to extend that principle to generic lambdas involves invasive, messy changes to the grammar.

  4. Section 15.27 currently defines the Expression grammar production. To reasonably accommodate the chapter structure without drastically changing section numbers, this grammar production has been moved to 15.2 and 15.27 has been repurposed to address lambda expressions.

15.27.1 Lambda Parameters [New]

The formal parameters of a lambda expression may have either declared types or inferred types. These styles cannot be mixed: it is not possible for a lambda expression to declare the types of some of its parameters but leave others to be inferred. Only parameters with declared types can have modifiers. [jsr335-15.27.1-10]

The syntax for formal parameters with declared types is the same as the syntax for the parameters of a method declaration (8.4.1). [jsr335-15.27.1-20]

The declared type of a formal parameter is denoted by the Type that appears in its parameter specifier, followed by any bracket pairs that follow the Identifier in the declarator, except for a variable arity parameter, whose declared type is an array type whose component type is the Type that appears in its parameter specifier. [jsr335-15.27.1-21]

The previous paragraph comes from 8.4.1 "Formal Parameters."

If the formal parameters have inferred types, then these types are derived (15.27.3) from the function type targeted by the lambda expression.

If an annotation a (9.7) on a formal parameter corresponds to an annotation type T (9.6), and T has a (meta-)annotation m that corresponds to annotation.Target, then m must have an element whose value is annotation.ElementType.PARAMETER, or a compile-time error occurs. [jsr335-15.27.1-22]

The scope and shadowing of a formal parameter is specified in 6.3 and 6.4. [jsr335-15.27.1-30]

It is a compile-time error for a lambda expression to declare two formal parameters with the same name. (That is, their declarations mention the same Identifier.) [jsr335-15.27.1-40]

It is a compile-time error if a formal parameter that is declared final is assigned to within the body of the lambda expression. [jsr335-15.27.1-50]

It is a compile-time error to use mixed array notation (10.2) for a variable arity parameter. [jsr335-15.27.1-60]

When the lambda expression is invoked (via a method invocation expression (15.12)), the values of the actual argument expressions initialize newly created parameter variables, each of the declared or inferred type, before execution of the lambda body. The Identifier that appears in the VariableDeclaratorId or the InferredFormalParameterList may be used as a simple name in the lambda body to refer to the formal parameter. [jsr335-15.27.1-70]

A lambda parameter of type float always contains an element of the float value set (4.2.3); similarly, a lambda parameter of type double always contains an element of the double value set. It is not permitted for a lambda parameter of type float to contain an element of the float-extended-exponent value set that is not also an element of the float value set, nor for a lambda parameter of type double to contain an element of the double-extended-exponent value set that is not also an element of the double value set. [jsr335-15.27.1-80]

The previous seven paragraphs are derived from 8.4.1 "Formal Parameters."
Discussion and motivation:
  1. When the lambda parameters' types are inferred, the same lambda expression body can be interpreted in many different ways, depending on the context in which it appears. Specifically, the types of expressions in the body (including return statement values), the checked exceptions thrown, and the type-correctness of the code all depend on the formal parameters' inferred types.

    There are two important implications. First, inference of parameter types must be independent of the lambda expression body (the alternative would be to do full constraint-producing type checking, which would be a lot more complex than the simple subtyping and conversion constraints that we handle now). Second, because lambda expressions can be method parameters, overload resolution may cause the same lambda body to be "hypothetically type-checked" more than once—that is, it may be type-checked multiple times, and an error that occurs during one of these times does not necessarily indicate that the program is incorrect.

  2. There is no distinction made between the following lambda parameter lists:
    (int... x) -> ..
    (int[] x) -> ..
    

    Consistent with the rules for overriding, either can be used, whether the functional interface's abstract method is varargs or not. Since lambda expressions are never directly invoked, introducing int... where the functional interface uses int[] can have no impact on the surrounding program. And, of course, in the lambda body, a varargs parameter is treated just like an array parameter.

15.27.2 Lambda Body [New]

A lambda body is either a single expression or a block (14.2). [jsr335-15.27.2-10]

Like a method body, a lambda body describes code that will be executed whenever an invocation occurs.

Unlike code appearing in anonymous class declarations, the meaning of names and the this and super keywords appearing in a lambda body, along with the accessibility of referenced declarations, are the same as in the surrounding context (except that lambda parameters introduce new names). [jsr335-15.27.2-20]

A block lambda body is void-compatible if every return statement in the block has the form return;. [jsr335-15.27.2-30]

A block lambda body is value-compatible if it cannot complete normally (14.21) and every return statement in the block has the form return Expression;. [jsr335-15.27.2-31]

It is a compile-time error if a block lambda body is neither void-compatible nor value-compatible. [jsr335-15.27.2-40]

Note that some block lambda bodies, such as { throw new RuntimeException(); } are both void-compatible and value-compatible.

In a value-compatible block lambda body, the result expressions are any expressions that may produce an invocation's value. Specifically, for each return statement (14.17) of the form return Expression ; contained by the body, the Expression is a result expression. [jsr335-15.27.2-50]

Any local variable, formal parameter, or exception handler parameter used but not declared in a lambda expression must be either declared final or effectively final (4.12.4). [jsr335-15.27.2-60]

Any local variable used but not declared in a lambda body must be definitely assigned (16) before the lambda body. [jsr335-15.27.2-70]

The previous two paragraphs mimic 8.1.3, Inner Classes and Enclosing Instances.
Examples of void- and value-compatible:
These are value-compatible:
() -> { return "done"; }
() -> { if (cond) return 1; else return 0; }

These are void-compatible:

() -> {}
() -> { System.out.println("done"); }

These are both:

() -> { throw new RuntimeException(); }
() -> { while (true); }

This is neither:

() -> { if (cond) return "done"; System.out.println("done"); }
Discussion and motivation:
  1. The transparency of this (both explicit and implicit) in the body of a lambda expression—that is, treating it the same as in the surrounding context—allows more flexibility for VM optimizations, and prevents the meaning of unqualified names in the body from being dependent on overload resolution.

    Practically speaking, it is unusual for a lambda to need to talk about itself (either to call itself recursively or to invoke its other methods), while it is more common to want to use names to refer to things in the enclosing class that would otherwise be shadowed (this, toString()). (This assertion is based on some analysis of existing code with anonymous inner classes.)

    If it is necessary for a lambda expression to refer to itself (as if via this), a method reference or an anonymous inner class should be used instead.

  2. The handling of void/value compatible and the meaning of names serve to minimize the dependency on a particular target type in the given context, which is useful both for implementations and for user understanding. While expressions can be assigned different types during overload resolution depending on the target type, the meaning of unqualified names and basic structure of the lambda body do not change.

    It's worth noting that the void/value compatible definition is not a strictly structural property: "can complete normally" depends on the values of constant expressions, and these may include names that reference constant variables.

3.9 Keywords [Modified]

Compare JLS 3.9

50 51 character sequences, formed from ASCII letters characters, are reserved for use as keywords and cannot be used as identifiers (3.8). [jls-3.9-100]

Keyword: one of
  '_'
  'abstract'
  'assert'
  ...
Discussion and motivation:
  1. The _ character has special meaning in other languages with lambda expressions; to avoid confusion, we reserve it as a keyword here. Future versions of Java may give the _ keyword special semantics—for example, to indicate an unnamed parameter.

3.12 Operators [Modified]

Compare JLS 3.12

37 38 tokens are the operators, formed from ASCII characters. [jls-3.12-100]

Operator: one of
  ...
  '>>='
  '>>>='
  '->'

4.12.3 Kinds of Variables [Modified]

Compare JLS 4.12.3

There are seven eight kinds of variables:

  1. A class variable ...
  2. An instance variable ...
  3. Array components ...
  4. Method parameters (8.4.1) name argument values passed to a method. For every parameter declared in a method declaration, a new parameter variable is created each time that method is invoked (15.12). The new variable is initialized with the corresponding argument value from the method invocation. The method parameter effectively ceases to exist when the execution of the body of the method is complete.
  5. Constructor parameters (8.8.1) name argument values passed to a constructor. For every parameter declared in a constructor declaration, a new parameter variable is created each time a class instance creation expression (15.9) or explicit constructor invocation (8.8.7) invokes that constructor. The new variable is initialized with the corresponding argument value from the creation expression or constructor invocation. The constructor parameter effectively ceases to exist when the execution of the body of the constructor is complete.
  6. Lambda parameters (15.27.1) name argument values passed to a lambda expression body (15.27.2). For every parameter declared in a lambda expression, a new parameter variable is created each time a method implemented by the lambda body is invoked (15.12). The new variable is initialized with the corresponding argument value from the method invocation. The lambda parameter effectively ceases to exist when the execution of the lambda expression body is complete.
  7. An exception parameter ...
  8. Local variables ...

...

4.12.4 Final Variables [Addendum]

See JLS 4.12.4

Certain variables that are not declared final may instead be considered effectively final.

A local variable or a method, constructor, lambda, or exception parameter is effectively final if it is not final but it never occurs as the left hand operand of an assignment operator (15.26) or as the operand of an increment or decrement operator (15.14, 15.15). [jsr335-4.12.4-10]

In addition, a local variable whose declaration lacks an initializer is effectively final if all of the following are true: [jsr335-4.12.4-20]

If a variable is effectively final, adding the final modifier to its declaration will not introduce any compile-time errors. Conversely, a local variable or parameter that is declared final in a valid program becomes effectively final if the final modifier is removed.

Examples of effectively final:
void m1(int x) {
   int y = 1;
   foo(() -> x+y);
   // Legal: x and y are both effectively final.
 }

 void m2(int x) {
   int y;
   y = 1;
   foo(() -> x+y);
   // Legal: x and y are both effectively final.
 }

 void m3(int x) {
   int y;
   if (..) y = 1;
   foo(() -> x+y);
   // Illegal: y is effectively final, but not definitely assigned.
 }

 void m4(int x) {
   int y;
   if (..) y = 1;
   else y = 2;
   foo(() -> x+y);
   // Legal: x and y are both effectively final.
 }

 void m5(int x) {
   int y;
   if (..) y = 1;
   y = 2;
   foo(() -> x+y);
   // Illegal: y is not effectively final.
 }

 void m6(int x) {
   foo(() -> x+1);
   x++;
   // Illegal: x is not effectively final.
 }

 void m7(int x) {
   foo(() -> x=1);
   // Illegal: x is not effectively final.
 }

 void m8() {
   int y;
   foo(() -> y=1);
   // Illegal: y is not definitely assigned before the lambda (see 15.27.2)
 }
Discussion and motivation:
  1. Mitigating the problem of inner classes (now lambda expressions) being unable to capture non-final local variables is one of the stated goals of this project (see "State of the Lambda").

    "Effectively final" is a concept introduced with Java SE 7 for improved exception handling. (Java Concurrency in Practice similarly discusses "effectively immutable" variables.) We've broadened the definition here to include all non-field variables.

  2. The effectively-final restriction prohibits access to dynamically-changing local variables, as the final restriction did before it; it simply reduces the clerical burden on programmers. Capture of mutable variables introduces many concurrency-related problems.

    This restriction includes standard loop variables, but not for-each loop variables, which are treated as distinct for each iteration of the loop (see 14.14.2). For background reading on how C# arrived at a similar interpretation of loop variables, see Eric Lippert's blog posts on the topic.

  3. Guaranteeing that effectively final and "legally declarable as final" are equivalent is tricky. The goal is that it should be possible to add or remove a final keyword to any variable that is initially either declared final or effectively final without impacting the validity of the program. (As far as variable capture is concerned, anyway; there are other parts of the language that, for various reasons, depend on a variable being explicitly final and an effectively-final variable cannot act as a substitute.)

6.3 Scope of a Declaration [Modified]

Compare JLS 6.3

...

The scope of a formal parameter of a method (8.4.1), constructor (8.8.1), or lambda expression (15.27) is the entire body of the method, constructor, or lambda expression. [jls-6.3-340]

...

6.4 Shadowing and Obscuring [Modified]

Compare JLS 6.4

A local variable (14.4), formal parameter (8.4.1, 15.27.1), exception parameter (14.20), and local class (14.3) can only be referred to using a simple name (6.2), not a qualified name (6.6). [jls-6.4-100]

Some declarations are not permitted within the scope of a local variable, formal parameter, exception parameter, or local class declaration because it would be impossible to distinguish between the declared entities using only simple names.

For example, if the name of a formal parameter of a method could be redeclared as the name of a local variable in the method body, then the local variable would shadow the formal parameter and the formal parameter would no longer be visible—an undesirable outcome.

It is a compile-time error if the name of a formal parameter is used to declare a new variable within the body of the method, constructor, or lambda expression, unless the new variable is declared within a class declaration contained by the method, constructor, or lambda expression. [jls-6.4-200]

It is a compile-time error if the name of a local variable v is used to declare a new variable within the scope of v, unless the new variable is declared within a class whose declaration is within the scope of v. [jls-6.4-210]

It is a compile-time error if the name of an exception parameter is used to declare a new variable within the Block of the catch clause, unless the new variable is declared within a class declaration contained by the Block of the catch clause. [jls-6.4-230]

It is a compile-time error if the name of a local class C is used to declare a new local class within the scope of C, unless the new local class is declared within another class whose declaration is within the scope of C. [jls-6.4-220]

It is a compile-time error if the name of a variable declared in a ResourceSpecification of a try-with-resources statement (14.20.3) is redeclared within the try Block as a local variable of the directly enclosing method, constructor, or initializer block, or as an exception parameter of a catch clause in a try statement of the directly enclosing method or initializer block. [jls-6.4-240]

The translation of a try-with-resources statement implies the rule above.

As an exception to the general restriction, these rules allow redeclaration in certain nested class declarations (i.e. local classes (14.3) and anonymous classes (15.9)) that occur within the variable's or local class's scope.

[Example] ...

Discussion and motivation:
  1. There are two design alternatives for handling name clashes created by lambda parameters and other variables declared in lambda expressions.
    • Mimicking class declarations: like local classes, lambda expressions introduce a new "level" for names, and all variable names outside the expression can be redeclared.
    • A "local" strategy: like catch clauses, for loops, and blocks, lambda expressions operate at the same "level" as the enclosing context, and local variables outside the expression cannot be shadowed.

    The above rules use the local strategy: there is no special dispensation that allows a variable declared in a lambda expression to shadow a variable declared in an enclosing method.

  2. The approach to expressing these rules is somewhat different than JLS 7, because phrases like "local variable of the directly enclosing method" do not very accurately describe local variables declared in a lambda body (possibly within multiple levels of nesting). The presentation above clearly distinguishes between declarations in nested class declarations (allowed) and declarations in nested lambda expressions (prohibited).
  3. The rule for ResourceSpecification variables was removed for clarity. Like the variable of an enhanced for loop, the resource variable is a local variable (i.e., it is not called out as a separate kind of variable in 4.12.3) and is covered by the local variable rule; this interpretation is further reinforced by the fact that the translation of try-with-resources generates a local variable, as alluded to in the redacted text.
  4. In principle, the revised rule for local classes is not general enough, as it does not make an exception for a class of the same name declared within the local class itself. However, this case is already prohibited by a separate rule: a class can never have the same name as a class that encloses it (8.1).

8.1.3 Inner Classes and Enclosing Instances [Modified]

Compare JLS 8.1.3

...

Any local variable, formal parameter, or exception parameter used but not declared in an inner class must be either declared final or effectively final (4.12.4). [jls-8.1.3-510]

...

Discussion and motivation:
  1. This extends support for effectively final variable capture to local classes (including anonymous classes) in addition to lambda expressions.

14.15 The break Statement [Modified]

Compare JLS 14.15

...

A break statement must refer to a label within the immediately enclosing method, constructor,initializer, or lambda body. There are no non-local jumps. If no labeled statement with Identifier as its label in the immediately enclosing method, constructor, initializer, or lambda body contains the break statement, a compile-time error occurs. [jls-14.15-320]

...

14.16 The continue Statement [Modified]

Compare JLS 14.16

...

A continue statement must refer to a label within the immediately enclosing method, constructor,initializer, or lambda body. There are no non-local jumps. If no labeled statement with Identifier as its label in the immediately enclosing method, constructor, initializer, or lambda body contains the continue statement, a compile-time error occurs. [jls-14.16-330]

...

14.17 The return Statement [Modified]

Compare JLS 14.17

A return statement returns control to the invoker of a method (8.4, 15.12) or constructor (8.8, 15.9). [jls-14.17.100]

ReturnStatement:
  'return' Expression_opt ';'

A return statement is contained in the innermost constructor, method, initializer, or lambda expression whose body encloses the return statement. [jls-14.17-120]

It is a compile-time error if a return statement is contained in an instance initializer or a static initializer (8.6, 8.7). [jls-14.17.200]

A return statement with no Expression must be contained in one of the following, or a compile-time error occurs: [jls-14.17-300]

A return statement with no Expression attempts to transfer control to the invoker of the method, constructor, or lambda body that contains it. To be precise, a return statement with no Expression always completes abruptly, the reason being a return with no value. [jls-14.17-310]

A return statement with an Expression must be contained in one of the following, or a compile-time error occurs: [jls-14.17-400]

The Expression must denote a variable or a value of some type T, or a compile-time error occurs. [jls-14.17-410]

When a return statement with an Expression appears in a method declaration, the Expression must be assignable (5.2.4) to the declared result type of the method, or a compile-time error occurs. [jls-14.17-420]

The equivalent assignable check for return statements in lambda expressions is covered in Part E (15.27.3).

A return statement with an Expression attempts to transfer control to the invoker of the method or lambda body that contains it; the value of the Expression becomes the value of the method invocation. More precisely, ... [jls-14.17-430]

...

14.18 The throw Statement [Modified]

Compare JLS 14.18

...

If a throw statement is contained in a method declaration or a lambda expression, but its value is not caught by some try statement that contains it, then the invocation of the method completes abruptly because of the throw. [jls-14.18-510]

...

14.20 The try Statement [Modified]

Compare JLS 14.20

...

In a uni-catch clause, an exception parameter that is not declared final (implicitly or explicitly) is considered effectively final if it never occurs within its scope as the left-hand operand of an assignment operator (15.26). [jls-14.20-620]

An implicitly final exception parameter is final by virtue of its declaration, while an effectively final exception parameter is (as it were) final by virtue of how it is used. An exception parameter of a multi-catch clause is implicitly final, so will never occur as the left-hand operand of an assignment operator, but is not considered effectively final.

If an exception parameter is effectively final (in a uni-catch clause) or implicitly final (in a multi-catch clause), then adding an explicit final modifier to its declaration will not introduce any compile-time errors. However, if the exception parameter of a uni-catch clause is explicitly declared final, then removing the final modifier may introduce compile-time errors. This is because the exception parameter, while still effectively final, can no longer be referenced by, for example, local classes. On the other hand, if there are no compile-time errors, it is possible to further change the program so that the exception parameter is re-assigned and no longer effectively final.

...

Discussion and motivation:
  1. The definition and discussion about effectively final is superseded by 4.12.4, above. The note about how the implicit finality of a multi-catch clause is different than effective finality is still useful, though.

15.8.3 this [Modified]

Compare JLS 15.8.3

The keyword this may be used only in the body of an instance method, instance initializer, or constructor, or in the initializer of an instance variable of a class. If it appears anywhere else, a compile-time error occurs. [jls-15.8.3-100]

When used as a primary expression, the keyword this denotes a value that is a reference to the object for which the instance method was invoked (15.12), or to the object being constructed. [jls-15.8.3-200]

Note that a lambda expression (15.27) is not a method declaration. The keyword this is only allowed in a lambda expression if it is allowed in the context in which the lambda expression appears; the object denoted by this appearing in a lambda body is the same as the object denoted by this in the surrounding context.

...

16 Definite Assignment [Modified]

Compare JLS 16

...

Throughout the rest of this chapter, we will, unless explicitly stated otherwise, write V to represent a local variable or a blank final field (for rules of definite assignment) or a blank final variable (for rules of definite unassignment).

...