Lambda expressions and closures

brief introduction

We usually talk about closures, which generally refers to the environment of JavaScript. Closure is a very important and commonly used concept in JS. The reason for closures is that the scope of variables is different. In general, the variables defined inside a function are visible only inside the function. If we want to manipulate this variable outside the function, we need to use closures.

More highlights:

Closures in JS

In JS, variables can be divided into two types: global scope and local scope. Local variables defined inside a function cannot be read outside the function.

  function f1(){
    var n=10;
  }
  alert(n); // error

In the above example, we define a local variable n in function F1 and try to access it from outside the function. Error in result.

Although the variables defined in the function cannot be accessed outside the function. But it can be accessed in the function defined in the function.

function f1(){
    var n=10;
    function f2(){
      alert(n);
    }
    return f2;
  }
  var result=f1();
  result(); // 10

In the above example, we defined F2 in F1 and accessed the local variable n in F2. Finally, F2 is returned. Then we can operate the returned function F2 to operate on the local variable n defined in the function.

So we get the definition of closure: a closure is a function defined inside a function, or a closure is a function that can access local variables of a function.

Closures in Java

Before lambda expressions appeared, there was no concept of functions in Java. What is almost equivalent to a function is a method.

Local variables of a method can be defined inside the method. We cannot define methods inside methods, but we can define anonymous classes inside methods. Then this anonymous class can access the local variables defined in the method. As shown in the following example:

public Runnable createClosureUsingClass(){
        int count=10;
        Runnable runnable= new Runnable() {
            @Override
            public void run() {
                System.out.println(count);
            }
        };
        return runnable;
    }

In the above method, we define a local variable count. Then an anonymous class runnable is created. In runnable, we access the local variable count.

Finally, return the created anonymous class. In this way, the returned anonymous class contains operations on method local variables, which is called closure.

In the lambda expression best practice, we introduced the differences between lambda expressions and anonymous classes:

In the inner class, a new scope will be created. Within this scope, you can define a new variable and reference it with this.

However, there is no new scope defined in the lambda expression. If this is used in the lambda expression, it points to an external class.

Although the direction of this is different, it is also a local variable of the method that can be accessed in the lambda expression:

public Runnable createClosureUsingLambda(){
        int count=10;
        Runnable runnable=()-> System.out.println(count);
        return runnable;
    }

In the above example, we accessed the defined count variable in the lambda expression.

Deep understanding of lambda expressions and local variables of functions

Firstly, lambda expression is stateless, because the essence of lambda expression is a function. Its function is to output a fixed result under the condition of given input parameters.

If the local variable in the method referenced in the lambda expression becomes a closure, because the lambda expression is stateful at this time. Let's use an example to illustrate.

The runnable created by the above lambda expression can be used as follows:

public void testClosureLambda(){
        Runnable runnable=createClosureUsingLambda();
        runnable.run();
    }

In order to deeply understand the relationship between lambda expression and local variable value transfer, we decompile the compiled class file.

javap -c -p ClosureUsage

Some output results are listed as follows:

  public java.lang.Runnable createClosureUsingLambda();
    Code:
       0: bipush        10
       2: istore_1
       3: iload_1
       4: invokedynamic #12,0             // InvokeDynamic #0:run:(I)Ljava/lang/invokedynamicinvokedynamic;
       9: astore_2
      10: aload_2
      11: areturn

private static void lambda$createClosureUsingLambda$0(int);
    Code:
       0: getstatic     #29                 // Field java/lang/System.out:Ljava/io/PrintStream;
       3: iload_0
       4: invokevirtual #35                 // Method java/io/PrintStream.println:(I)V
       7: return

Above, we listed the decompiled results of createlosureusinglambda and its internal lambda expression.

As you can see, in the createlosureusinglambda method, we first define an int with a value of 10 and put it on the stack.

Looking at the method generated by lambda expression, we can see that this method has an additional int parameter, and the parameter is passed in through the getstatic command.

This is how lambda expressions transfer state.

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>