Lambda表达式和闭包Closure
文章目錄
- 簡介
- JS中的閉包
- java中的閉包
- 深入理解lambda表達式和函數的局部變量
- 總結
簡介
我們通常講到閉包,一般都是指在javascript的環境中。閉包是JS中一個非常重要的也非常常用的概念。閉包產生的原因就是變量的作用域范圍不同。一般來說函數內部的定義的變量只有函數內部可見。如果我們想要在函數外部操作這個變量就需要用到閉包了。
更多精彩內容且看:
- 區塊鏈從入門到放棄系列教程-涵蓋密碼學,超級賬本,以太坊,Libra,比特幣等持續更新
- Spring Boot 2.X系列教程:七天從無到有掌握Spring Boot-持續更新
- Spring 5.X系列教程:滿足你對Spring5的一切想象-持續更新
- java程序員從小工到專家成神之路(2020版)-持續更新中,附詳細文章教程
更多內容請訪問www.flydean.com
JS中的閉包
在JS中,變量可以分為兩種全局作用域和局部作用域。在函數外部無法讀取函數內部定義的局部變量。
function f1(){var n=10;} alert(n); // error上面的例子中,我們在函數f1中定義了一個局部變量n,然后嘗試從函數外部訪問它。結果出錯。
雖然函數中定義的變量在函數外部無法被訪問。但是在函數中定義的函數中可以訪問呀。
function f1(){var n=10;function f2(){ alert(n);}return f2;}var result=f1(); result(); // 10上面的例子中,我們在f1中定義了f2,在f2中訪問了局部變量n。最后將f2返回。接著我們可以操作返回的函數f2來對函數中定義的局部變量n進行操作。
所以我們得出了閉包的定義:閉包就是定義在函數內部的函數,或者閉包是能夠訪問函數局部變量的函數。
java中的閉包
在lambda表達式出現之前,java中是沒有函數的概念的。和函數差不多相當的就是方法了。
在方法內部可以定義方法的局部變量。我們無法在方法內部定義方法,但是我們可以在方法內部定義匿名類。那么這個匿名類是可以訪問方法中定義的局部變量的。如下例所示:
public Runnable createClosureUsingClass(){int count=10;Runnable runnable= new Runnable() {@Overridepublic void run() {System.out.println(count);}};return runnable;}在上面的方法中,我們定義了一個局部變量count。然后創建了一個匿名類runnable。在runnable中,我們訪問了局部變量count。
最后將這個創建的匿名類返回。這樣返回的匿名類就包含了對方法局部變量的操作,這樣就叫做閉包。
在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 102: istore_13: iload_14: invokedynamic #12, 0 // InvokeDynamic #0:run:(I)Ljava/lang/invokedynamicinvokedynamic;9: astore_210: aload_211: areturnprivate static void lambda$createClosureUsingLambda$0(int);Code:0: getstatic #29 // Field java/lang/System.out:Ljava/io/PrintStream;3: iload_04: invokevirtual #35 // Method java/io/PrintStream.println:(I)V7: return上面我們列出了createClosureUsingLambda和它內部的lambda表達式的反編譯結果。
可以看到在createClosureUsingLambda方法中,我們首先定義了一個值為10的int,并將其入棧。
再看lambda表達式生成的方法,我們可以看到這個方法多出了一個int參數,并且通過getstatic命令將參數傳遞進來。
這就是lambda表達式傳遞狀態的原理。
總結
本文介紹了閉包和lambda表達式之間的關系,并從字節碼的角度進一步說明了局部變量是怎么傳遞給函數內部的lambda表達式的。
本文的例子https://github.com/ddean2009/
learn-java-base-9-to-20
本文作者:flydean程序那些事
本文鏈接:http://www.flydean.com/java-lambda-closure/
本文來源:flydean的博客
歡迎關注我的公眾號:程序那些事,更多精彩等著您!
總結
以上是生活随笔為你收集整理的Lambda表达式和闭包Closure的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小师妹学JavaIO之:文件编码和字符集
- 下一篇: 小师妹学JavaIO之:文件写入那些事