Lambda表达式和闭包Closure

简介

咱们一般讲到闭包,通常都是指在javascript的环境中。闭包是JS中一个很是重要的也很是经常使用的概念。闭包产生的缘由就是变量的做用域范围不一样。通常来讲函数内部的定义的变量只有函数内部可见。若是咱们想要在函数外部操做这个变量就须要用到闭包了。javascript

更多精彩内容且看:java

更多内容请访问 www.flydean.com

JS中的闭包

在JS中,变量能够分为两种全局做用域和局部做用域。在函数外部没法读取函数内部定义的局部变量。git

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

上面的例子中,咱们在函数f1中定义了一个局部变量n,而后尝试从函数外部访问它。结果出错。程序员

虽然函数中定义的变量在函数外部没法被访问。可是在函数中定义的函数中能够访问呀。github

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

上面的例子中,咱们在f1中定义了f2,在f2中访问了局部变量n。最后将f2返回。接着咱们能够操做返回的函数f2来对函数中定义的局部变量n进行操做。闭包

因此咱们得出了闭包的定义:闭包就是定义在函数内部的函数,或者闭包是可以访问函数局部变量的函数。ide

java中的闭包

在lambda表达式出现以前,java中是没有函数的概念的。和函数差很少至关的就是方法了。函数

在方法内部能够定义方法的局部变量。咱们没法在方法内部定义方法,可是咱们能够在方法内部定义匿名类。那么这个匿名类是能够访问方法中定义的局部变量的。以下例所示:区块链

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

在上面的方法中,咱们定义了一个局部变量count。而后建立了一个匿名类runnable。在runnable中,咱们访问了局部变量count。this

最后将这个建立的匿名类返回。这样返回的匿名类就包含了对方法局部变量的操做,这样就叫作闭包。

Lambda表达式最佳实践中,咱们介绍了lambda表达式和匿名类的不一样之处在于:

在内部类中,会建立一个新的做用域范围,在这个做用域范围以内,你能够定义新的变量,而且能够用this引用它。

可是在Lambda表达式中,并无定义新的做用域范围,若是在Lambda表达式中使用this,则指向的是外部类。

虽然this的指向是不一样的,可是在lambda表达式中也是能够访问方法的局部变量:

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

上面的例子中,咱们在lambda表达式中访问了定义的count变量。

深刻理解lambda表达式和函数的局部变量

首先lambda表达式是无状态的,由于lambda表达式的本质是函数,它的做用就是在给定输入参数的状况下,输出固定的结果。

若是lambda表达式中引用的方法中的局部变量,则lambda表达式就变成了闭包,由于这个时候lambda表达式是有状态的。咱们接下来用个例子来具体说明。

上面的lambda表达式建立的Runnable,咱们能够这样使用:

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

为了深刻理解lambda表达式和局部变量传值的关系,咱们将编译好的class文件进行反编译。

javap -c -p ClosureUsage

将部分输出结果列出以下:

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

上面咱们列出了createClosureUsingLambda和它内部的lambda表达式的反编译结果。

能够看到在createClosureUsingLambda方法中,咱们首先定义了一个值为10的int,并将其入栈。

再看lambda表达式生成的方法,咱们能够看到这个方法多出了一个int参数,而且经过getstatic命令将参数传递进来。

这就是lambda表达式传递状态的原理。

总结

本文介绍了闭包和lambda表达式之间的关系,并从字节码的角度进一步说明了局部变量是怎么传递给函数内部的lambda表达式的。

本文的例子[https://github.com/ddean2009/
learn-java-base-9-to-20](https://github.com/ddean2009/...

本文做者:flydean程序那些事

本文连接:http://www.flydean.com/java-lambda-closure/

本文来源:flydean的博客

欢迎关注个人公众号:程序那些事,更多精彩等着您!

相关文章
相关标签/搜索