Why java lambda expressions do not introduce a new level of reach?

As I understand it, in languages ​​like Haskell, and also as part of the lambda calculus, each lambda expression has its own scope, so if I have nested lambda expressions like: \x -> (\x -> x) , then the first parameter \x is different from the second \x .

In Java, if you do this, you will get a compilation error, as if you would use x again as the parameter name or the local variable name in lambda if it was already used inside the enclosing area, for example. as a parameter to the method.

Does anyone know why Java implemented lambda expressions in this way - why don't they introduce a new level of reach and behave like an anonymous class? I suppose this is due to some kind of limitation or optimization, or, perhaps, because lambdas needed to be hacked into an existing language?

+6
source share
2 answers

This is the same behavior as for other blocks of code in Java.

This gives a compilation error

 int a; { int a; } 

until it

 { int a; } { int a; } 

You can read about this section in the section of section 6.4 of JLS along with some considerations.

+10
source

A lambda block is a new block, for example a region, but it does not set a new context / level, as an anonymous class implementation does.

From the Java language specification 15.27.2 Lambda Body :

Unlike the code that appears in anonymous class declarations, the meaning of the this and super names and keywords that appear in the lambda body, as well as the availability of link declarations, is the same as in the surrounding context (except that the lambda parameters introduce new names) .

And from JLS 6.4 Shadow and Trimming :

These rules allow you to override a variable or local class in nested class declarations (local classes ( §14.3 ) and anonymous classes ( §15.9 )) that occur in the scope of a variable or local class. Thus, the declaration of a formal parameter, a local variable, or a local class can be obscured by a class declaration embedded in a method, constructor, or lambda expression; and the declaration of the exception parameter may be obscured by the declaration of the class nested in the Block clause of catch.

There are two design options for handling name conflicts created by lambda parameters and other variables declared in lambda expressions. One of them is to simulate class declarations: for example, local classes, lambda expressions introduce a new “level” for names, and all variable names outside the expression can be redefined. Another is a “local” strategy: such as catch locks for loops and blocks, lambda expressions work at the same level as the enclosing context, and local variables outside the expression cannot be obscured. The rules above use a local strategy; There is no special permission that allows a variable declared in a lambda expression to obscure a variable declared in an application.

Example:

 class Test { private int f; public void test() { int a; a = this.f; // VALID { int a; // ERROR: Duplicate local variable a a = this.f; // VALID } Runnable r1 = new Runnable() { @Override public void run() { int a; // VALID (new context) a = this.f; // ERROR: f cannot be resolved or is not a field // (this refers to the instance of Runnable) a = Test.this.f; // VALID } }; Runnable r2 = () -> { int a; // ERROR: Lambda expression local variable a cannot redeclare another local variable defined in an enclosing scope. a = this.f; // VALID }; } } 
+3
source

All Articles