閉包之Java lambda表達(dá)式淺析

1.首先仓蛆,什么是閉包

先看這么一個(gè)圖:

G,F,N 分別代表三個(gè)層次的函數(shù)笤闯,a,b,c分別是其中的變量。正常情況下阁猜,G無法訪問到N中的c聘惦。但是琳彩,通過閉包G可以訪問到c。這是一般對(duì)閉包的初體驗(yàn)部凑,下面看看維基對(duì)閉包的定義。

維基的定義:在計(jì)算機(jī)科學(xué)中碧浊,閉包(英語:Closure)涂邀,又稱詞法閉包(Lexical Closure)或函數(shù)閉包(function closures),是引用了自由變量的函數(shù)箱锐。這個(gè)被引用的自由變量將和這個(gè)函數(shù)一同存在比勉,即使已經(jīng)離開了創(chuàng)造它的環(huán)境也不例外。所以驹止,有另一種說法認(rèn)為閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實(shí)體浩聋。閉包在運(yùn)行時(shí)可以有多個(gè)實(shí)例,不同的引用環(huán)境和相同的函數(shù)組合可以產(chǎn)生不同的實(shí)例臊恋。

看起來意見還不統(tǒng)一衣洁,總結(jié)一下:

1.閉包是一個(gè)函數(shù),它引用了自由變量并和這個(gè)函數(shù)包在一起抖仅。

2.閉包不是函數(shù)坊夫,他是函數(shù)和引用環(huán)境組成的實(shí)體砖第。

這兩個(gè)定義其實(shí)差不多,只是在糾結(jié)閉包是否是一個(gè)函數(shù)环凿。第二種較為全面梧兼,理由是函數(shù)是一些可執(zhí)行的代碼,這些代碼在編寫時(shí)就定義了并且只有一個(gè)實(shí)例智听,但是閉包會(huì)因?yàn)橐玫淖兞坎煌a(chǎn)生不同的實(shí)例羽杰。因此閉包和函數(shù)還是有差別的,它只是像函數(shù)到推,但并不是函數(shù)考赛。

以python為例說明閉包有多個(gè)實(shí)例:


這個(gè)例子里,函數(shù)make_add返回一個(gè)lambda环肘,lambda可以通過不同的參數(shù)來生成不同的閉包實(shí)例欲虚,其中的參數(shù)就是引用變量,當(dāng)然引用變量不止這些悔雹,還包括所有能引用到的變量复哆,這些引用變量被稱為上值(upvalue)。

構(gòu)成閉包其實(shí)只要有兩點(diǎn):

1.函數(shù)的代碼邏輯

2.引用環(huán)境

引用環(huán)境是什么呢腌零?

引用環(huán)境是一組變量的定義

identifer----value

identifer----value

identifer----value

.......

以上就是一個(gè)引用環(huán)境梯找,詞法作用域的語言中,在函數(shù)取值時(shí)益涧,會(huì)捕捉函數(shù)定義時(shí)能引用到的所有變量锈锤,將其組成一個(gè)新的引用環(huán)境。和這個(gè)函數(shù)一起闲询,就組成了閉包久免。由于每種語言實(shí)現(xiàn)引用環(huán)境有所不同,對(duì)閉包的理解便有所不同扭弧。在回到開始這個(gè)例子阎姥,N為了維持它的詞法域,將他所有能訪問到的局部變量都包含在了一起鸽捻。

2.閉包的條件

從上面的定義可以得知

1.函數(shù)可以嵌套定義

2.可以定義匿名函數(shù)

3.upvalue

4.函數(shù)和upvalue可以組成一個(gè)可調(diào)用的實(shí)體呼巴。

5.函數(shù)是一種數(shù)據(jù)類型,可以賦值御蒲,可以作為參數(shù)衣赶,也可以做為返回值。

3.Java中的閉包

從上面的閉包條件可知厚满,閉包幾乎是函數(shù)式編程的專利府瞄,Java作為強(qiáng)制的面向?qū)ο缶幊陶Z言怎么可能有閉包,先不管碘箍!看看Java是如何實(shí)現(xiàn)閉包的摘能。

a.語法

首先要聲明一個(gè)函數(shù)式接口,比如Runnable:

@FunctionalInterface

public interface Runnable {

public abstract void run();

}

函數(shù)式接口只允許有一個(gè)函數(shù)续崖,如果你有一個(gè)接口,并且這個(gè)接口只有一個(gè)方法团搞,即便你沒有@FunctionalInterface严望,編譯器也會(huì)認(rèn)為這是一個(gè)函數(shù)接口。但是建議還是@FunctionalInterface一下逻恐,如果其他人在這個(gè)接口下添加方法像吻,IDE會(huì)告訴他:你這么做是非法的。

有了這個(gè)函數(shù)式接口就可以用Lambda表達(dá)式了复隆,格式如下

(參數(shù)) -> 表達(dá)式

如果沒有參數(shù)也可以簡(jiǎn)化為()拨匆,如果只有一個(gè)參數(shù),可以不要括號(hào)

表達(dá)式需要用{}塊起來挽拂,如果只有一行代碼則不需要惭每。

比如:

new Thread(() -> System.out.println("Hello world!"));

1.? () -> 5? ? ? ? ? ? ? ? ? ? ? ? ? // return都不要寫

2.? x -> 2 * x? ? ? ? ? ? // 括號(hào)都可以不要

3.? (x, y) -> x – y? ? ? ? ? ? ? ? ? ? // 參數(shù)聲明也可以免寫

4.? (int x, int y) -> x + y? ? ? // 你也可以聲明

5.? (String s) -> System.out.print(s) // 大括號(hào)也懶得打了

還可以用::兩個(gè)冒號(hào)來簡(jiǎn)寫

1.System.out::println? ? //非靜態(tài)方法引用

2.String::valueOf? ? //靜態(tài)方法引用

3.ArrayList::new? ? //構(gòu)造函數(shù)的引用

b.特性

特性1:不允許修改局部變量

比如以下寫法是不允許的

int n= 1;

new Thread(() -> n++);

提示:Local variable n defined in an enclosing scope must be final or effectively final

把n改成final就可以了,如果你不修改局部變量的值并不是需要final。如果沒有局部變量的捕獲亏栈,據(jù)說這樣的閉包要高效一些台腥。

特性2:參數(shù)也必須是final

特性3:可以訪問并修改外部類的變量

特性4:不能將異常拋出

驚!看起來就和內(nèi)部類一樣绒北!

c.分析

函數(shù)式編程中黎侈,函數(shù)是第一公民,可以被嵌套定義闷游,也可以被當(dāng)作參數(shù)使用峻汉,就像面向?qū)ο缶幊痰念愐粯印T俾?lián)系上文提到的閉包條件脐往,我們發(fā)現(xiàn)Java作為一種強(qiáng)制面向?qū)ο蟮恼Z言休吠,幾乎是不能有閉包的,但是如果將函數(shù)改為類业簿,那么情況就不一樣了瘤礁。事實(shí)上Java 8也就是這么干的。

函數(shù)式接口規(guī)定只能有一個(gè)方法辖源,目的在于使這個(gè)類的作用就像一個(gè)函數(shù)一樣。僅此一步希太,閉包的條件就滿足了克饶。

d.思考

1.和內(nèi)部類的異同

異:

不會(huì)生成新的類

this不同

同:

不能改變局部變量的值

可以改變?nèi)肿兞康闹?/p>

2.既然匿名內(nèi)部類和閉包那么相同,那么內(nèi)部類也可以算是一個(gè)閉包嗎誊辉?

有種觀點(diǎn)認(rèn)為矾湃,匿名內(nèi)部類是面向?qū)ο蟮拈]包,理由是內(nèi)部類不僅包含了代碼邏輯還包含了外圍類的引用堕澄,符合閉包的定義邀跃。Oracle官網(wǎng)也提到:

Lambda expressions to replace anonymous inner classes in Java SE 8.

他就是為了解決匿名類的霉咨。

另一個(gè)佐證:在Java還沒支持閉包之前,就有人提出修改匿名內(nèi)部類的創(chuàng)建方式來實(shí)現(xiàn)閉包拍屑。

3又是一個(gè)語法糖?

并不是途戒,同樣一個(gè)接口,用內(nèi)部類實(shí)現(xiàn)僵驰,會(huì)多出一個(gè)class文件來喷斋,如果用閉包則不會(huì)。

4.閉包的優(yōu)缺點(diǎn)

閉包有更加簡(jiǎn)潔的語法蒜茴,更清晰的邏輯星爪,也更加抽象。但是閉包的內(nèi)存開銷會(huì)大一些粉私,也不要濫用顽腾。

e.應(yīng)用

Java8的Function包有一大堆的函數(shù)式接口,以供使用

http://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html

如forEach,你現(xiàn)在不需要寫一個(gè)函數(shù)诺核,你只需要這樣做就可以了

list.forEach(System.out::println);

方法的實(shí)現(xiàn):

//Consumer是function包下的一個(gè)函數(shù)式接口抄肖。

default void forEach(Consumer action) {

Objects.requireNonNull(action);

for (T t : this) {

action.accept(t);

}

}

Stream是是目前Java Lamdba表達(dá)式的重頭戲,Stream并不是IO流猪瞬,而是對(duì)集合集合操作的迭代憎瘸,普通的Iterator只是對(duì)集合元素一個(gè)一個(gè)的遍歷,而Stream不僅實(shí)現(xiàn)了遍歷陈瘦,還包含了對(duì)元素操作的描述幌甘,如過濾,內(nèi)類轉(zhuǎn)換等痊项。這些描述就是Lamdba表達(dá)式锅风。

下面看一個(gè)官網(wǎng)的示例:

int sum = widgets.stream()

.filter(b -> b.getColor() == RED)

.mapToInt(b -> b.getWeight())

.sum();

上例中,我們需要獲取widgets集合中顏色為紅色的元素鞍泉,并將寬度轉(zhuǎn)換為Int再求和皱埠,這個(gè)例子看起來就像我們使用AlertDialog.Builder創(chuàng)建Dialog一樣,每個(gè)setXX方法都返回Builder對(duì)象咖驮,最終通過create方法創(chuàng)建真正的Dialog边器。

Stream有兩種形式:

1.Intermediate

Intermediate是中間操作,其后可以再跟Stream操作托修,如上例的filter忘巧,就像Builder一樣。辨別Intermediate可以通過方法是否返回Stream來辨別睦刃,Intermediate的操作不會(huì)執(zhí)行真正的遍歷砚嘴,它的目的是打開流,并包含了這樣的操作,再返回新的流交給下一個(gè)操作使用际长。

2.Terminal

最終操作耸采,其后不能再跟Stream操作,就像Create一樣工育。Terminal操作返回值不是Stream虾宇,可能是一個(gè)數(shù)據(jù)類型或者Void,如上例的sum,返回的值是Int翅娶。因此Terminal是真正執(zhí)行遍歷的地方文留。

Strem的特性:

1.沒有存儲(chǔ),流不是一個(gè)數(shù)據(jù)結(jié)構(gòu)竭沫,是一個(gè)數(shù)據(jù)操作的管道(through a pipeline of computational operations.)燥翅。每一次值轉(zhuǎn)換都生成一個(gè)新的Stream對(duì)象,因此操作可以向鏈條一樣形成一個(gè)管道蜕提。


流的實(shí)現(xiàn)類是AbstractPipeline森书,其主要的屬性有:

private Spliterator sourceSpliterator;//資源的迭代器,包含了集合

private final AbstractPipeline sourceStage;//自身Stream

private final AbstractPipeline previousStage;//上一個(gè)Stream

private AbstractPipeline nextStage;//下一個(gè)Stream

2.不修改源數(shù)據(jù)

private final Collection collection; // null OK

3.無界操作,即Short-circuiting谎势,他的作用是在無限的集合中返回有限的流或者在有限的時(shí)間內(nèi)完成操作凛膏。這一類的方法有anyMatch、 allMatch脏榆、 noneMatch猖毫、 findFirst、 findAny须喂、 limit吁断。

4.中間操作是懶惰的

5.只能執(zhí)行一次最終操作

6.并行,采用fork/join框架

list.parallelStream();

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末坞生,一起剝皮案震驚了整個(gè)濱河市仔役,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌是己,老刑警劉巖又兵,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異卒废,居然都是意外死亡沛厨,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門摔认,熙熙樓的掌柜王于貴愁眉苦臉地迎上來逆皮,“玉大人,你說我怎么就攤上這事级野∫惩溃” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵蓖柔,是天一觀的道長(zhǎng)辰企。 經(jīng)常有香客問我,道長(zhǎng)况鸣,這世上最難降的妖魔是什么牢贸? 我笑而不...
    開封第一講書人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮镐捧,結(jié)果婚禮上潜索,老公的妹妹穿的比我還像新娘。我一直安慰自己懂酱,他們只是感情好竹习,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著列牺,像睡著了一般整陌。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瞎领,一...
    開封第一講書人閱讀 51,370評(píng)論 1 302
  • 那天泌辫,我揣著相機(jī)與錄音,去河邊找鬼九默。 笑死震放,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的驼修。 我是一名探鬼主播殿遂,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼邪锌!你這毒婦竟也來了勉躺?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤觅丰,失蹤者是張志新(化名)和其女友劉穎饵溅,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體妇萄,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蜕企,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了冠句。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片轻掩。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖懦底,靈堂內(nèi)的尸體忽然破棺而出唇牧,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布丐重,位于F島的核電站腔召,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏扮惦。R本人自食惡果不足惜臀蛛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望崖蜜。 院中可真熱鬧浊仆,春花似錦、人聲如沸豫领。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽等恐。三九已至沙绝,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鼠锈,已是汗流浹背闪檬。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留购笆,地道東北人粗悯。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像同欠,于是被迫代替她去往敵國(guó)和親样傍。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理铺遂,服務(wù)發(fā)現(xiàn)衫哥,斷路器,智...
    卡卡羅2017閱讀 134,656評(píng)論 18 139
  • 原文http://www.codeceo.com/article/learn-java-lambda.html L...
    與我常在1053閱讀 1,130評(píng)論 1 7
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法襟锐,類相關(guān)的語法撤逢,內(nèi)部類的語法,繼承相關(guān)的語法粮坞,異常的語法蚊荣,線程的語...
    子非魚_t_閱讀 31,631評(píng)論 18 399
  • 原文地址:http://stackoverflow.com/documentation/java/91/lambd...
    王永迪閱讀 7,814評(píng)論 6 17
  • 當(dāng)世界向你開火,你痛而不言莫杈, 回頭望去互例,足跡清晰。 你豎立盔甲筝闹,保持著最后的尊嚴(yán)媳叨, 告訴自己腥光,“我只是輸給了時(shí)間”...
    講真書畫閱讀 275評(píng)論 5 2