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的博客

歡迎關注個人公衆號:程序那些事,更多精彩等着您!

相關文章
相關標籤/搜索