Kotlin 知識梳理(5) - lambda 表達式和成員引用

Kotlin 知識梳理系列文章

Kotlin 知識梳理(1) - Kotlin 基礎(chǔ)
Kotlin 知識梳理(2) - 函數(shù)的定義與調(diào)用
Kotlin 知識梳理(3) - 類葱椭、對象和接口
Kotlin 知識梳理(4) - 數(shù)據(jù)類、類委托 及 object 關(guān)鍵字
Kotlin 知識梳理(5) - lambda 表達式和成員引用
Kotlin 知識梳理(6) - Kotlin 的可空性
Kotlin 知識梳理(7) - Kotlin 的類型系統(tǒng)
Kotlin 知識梳理(8) - 運算符重載及其他約定
Kotlin 知識梳理(9) - 委托屬性
Kotlin 知識梳理(10) - 高階函數(shù):Lambda 作為形參或返回值
Kotlin 知識梳理(11) - 內(nèi)聯(lián)函數(shù)
Kotlin 知識梳理(12) - 泛型類型參數(shù)


一罗标、本文概要

本文是對<<Kotlin in Action>>的學習筆記,如果需要運行相應(yīng)的代碼可以訪問在線環(huán)境 try.kotlinlang.org,這部分的思維導圖為:

二两芳、Lambda 表達式和成員引用

Lambda表達式,本質(zhì)上是可以 傳遞給函數(shù)的一小段代碼去枷,可以輕松地把通用的代碼結(jié)構(gòu)抽取成庫函數(shù)怖辆,Kotlin標準庫就大量地使用了它們。

2.1 Lambda 簡介:作為函數(shù)參數(shù)的代碼塊

Lambda的應(yīng)用場景有:

  • 當一個事件發(fā)生的時候運行這個事件處理器
  • 把這個操作應(yīng)用到這個數(shù)據(jù)結(jié)構(gòu)中所有的元素上

Java中删顶,可以用匿名內(nèi)部類來實現(xiàn)竖螃,但是它的語法很啰嗦,下面我們演示用Lambda來實現(xiàn)點擊監(jiān)聽:

button.setOnClickListener { /* 點擊后執(zhí)行的動作 */}

2.2 Lambda 和集合

我們對集合執(zhí)行的大部分任務(wù)都遵循幾個通用的模式逗余,所以實現(xiàn)這幾個模式的代碼應(yīng)該放在一個庫里特咆,下面我們演示一個例子:將Person數(shù)據(jù)類放到一個集合當中,并從中選出年齡最大的一個人录粱。


運行結(jié)果為:

這上面的例子用到了集合上的maxBy函數(shù)腻格,它只需要一個實參:一個函數(shù),指定比較哪個值來找到最大的元素啥繁,花括號中的代碼{ it.age }就是實現(xiàn)了這個邏輯的lambda菜职,它接收一個集合中的元素作為實參(使用it引用它)并且返回用來比較的值,在上面的例子中:

  • 集合元素是Person對象
  • 用來比較的值是存儲在其age屬性中的值

如果lambda剛好是 函數(shù)或者屬性的委托旗闽,可以用 成員引用 替換酬核。

people.maxBy(Person :: age)

2.3 Lambda 表達式語法

一個Lambda表達式把一小段行為進行編碼,你能把它 當做值到處傳遞适室,它可以被 獨立地聲明并存儲到一個變量中嫡意,但是最常見的還是直接聲明它并傳遞給函數(shù),下面是一個Lambda表達式的語法亭病,->前為 參數(shù)鹅很,后為 函數(shù)體嘶居,始終用 花括號包圍罪帖。

{x : Int, y : Int -> x + y}

2.3.1 將 Lambda 表達式存儲在變量中

可以將Lambda表達式存儲在一個變量中,把這個變量當做普通函數(shù)對待(即通過相應(yīng)的實參調(diào)用它):

2.3.2 直接調(diào)用 Lambda 表達式

如果需要把一小段代碼封閉在一個代碼塊中邮屁,可以使用庫函數(shù)run整袁,這種調(diào)用和內(nèi)建語言結(jié)構(gòu)一樣高效且不會帶來額外運行時開銷:


運行結(jié)果為:

2.3.3 Lambda 表達式的簡化過程

現(xiàn)在,讓我們回到最開始尋找集合中年齡最大的人的例子佑吝,它原本的調(diào)用方法如下坐昙,maxBy函數(shù)接收一個lambda表達式{ p : Person -> p.age }作為參數(shù):

people.maxBy({ p : Person -> p.age })

上面這段代碼的解釋為:花括號中的代碼片段是lambda表達式,把它作為實參傳給函數(shù)芋忿,這個lambda接收一個類型為Person的參數(shù)并返回它的年齡炸客。下面疾棵,我們一起來看一下如何簡化這個表達式:

  • 第一步:Kotlin有一個語法規(guī)定,如果lambda表達式是函數(shù)調(diào)用的 最后一個實參痹仙,它可以 放到括號的外邊是尔,因此上面的例子簡化為:
//第一步:將 lambda 表達式放到括號的外邊。
people.maxBy() { p : Person -> p.age }
  • 第二步:當lambda是函數(shù) 唯一的實參开仰,還可以 去掉調(diào)用代碼中的空括號對
//第二步:去掉空括號對拟枚。
people.maxBy { p : Person -> p.age }
  • 第三步:和局部變量一樣,如果lambda參數(shù)的類型可以被推倒出來众弓,你就不需要顯示地指定它恩溅,以maxBy函數(shù)為例,其 參數(shù)類型始終和集合的元素類型相同谓娃,因此編譯器知道你是對Person對象的集合調(diào)用maxBy函數(shù)脚乡,可以簡化為:
//第三步:省略 lambda 參數(shù)類型。
people.maxBy { p -> p.age }

但是如果我們用變量存儲lambda滨达,那么就沒有可以推斷出參數(shù)類型的上下文每窖,所以你必須顯示地指定參數(shù)類型:

//無法推斷出參數(shù)的類型,必須顯示地指定參數(shù)的類型弦悉。
val getAge = { p : Person -> p.age }
people.maxBy (getAge)
  • 第四步:如果當前上下文期望的是 只有一個參數(shù)的 lambda 窒典,并且這個參數(shù)的類型可以推斷出來,那么可以使用默認參數(shù)名稱it代替命名參數(shù):
//第四步:使用默認參數(shù)名稱稽莉。
people.maxBy { it.age }

2.3.4 又見 joinToString 函數(shù)

Kotlin 知識梳理(2) - 函數(shù)的定義與調(diào)用 中瀑志,我們通過joinToString介紹了命名參數(shù)和默認參數(shù)值的用法,實際上在標準庫中也有定義這個函數(shù)污秆,不同之處在于它可以接收一個附加的函數(shù)參數(shù)劈猪,這個函數(shù)可以用toString函數(shù)以外的方法來把一個 元素轉(zhuǎn)換成字符串,下面顯示如何 只打印出人的名字


運行結(jié)果為:

2.3.5 lambda 表達式包含更多語句

lambda表達式可以包含更多的語句良拼,最后一個表達式就是lambda的結(jié)果:


運行結(jié)果為:

2.4 在作用域中訪問變量

當在函數(shù)內(nèi)聲明一個匿名內(nèi)部類時战得,能夠在這個匿名內(nèi)部類引用這個函數(shù)的參數(shù)和局部變量,也可以用lambda做同樣的事情庸推,如果在函數(shù)內(nèi)部使用lambda常侦,也可以訪問這個函數(shù)的參數(shù),還有在lambda之前定義的局部變量贬媒。

下面我們用標準庫函數(shù)forEach來展示這種行為聋亡,它是最基本的集合操作函數(shù)之一:它所做的全部事情就是在集合中的每一個元素之上都調(diào)用給定的lambda


運行結(jié)果為:

2.4.1 在 lambda 中改變局部變量

Kotlin中不會僅限于訪問final變量,在lambda內(nèi)部也可以修改這些變量际乘,下面的代碼中對給定的相應(yīng)狀態(tài)碼set分別進行計數(shù)坡倔。


Kotlin中,它允許在lambda內(nèi)部訪問非final變量甚至修改它們。從lambda內(nèi)訪問外部變量罪塔,我們稱這些 變量被 lambda 捕捉投蝉,就像上面例子中的clientErrorsserverErrors

默認情況下征堪,局部變量的生命周期被限制在聲明這個局部變量的函數(shù)當中墓拜,但是如果它被lambda捕捉了,使用這個變量的代碼可以被存儲并稍后執(zhí)行请契,原理為:

  • 當捕捉final變量時咳榜,它的值和使用這個值的lambda代碼一起存儲。
  • 對非final變量爽锥,它的值被封裝在一個包裝器中涌韩,這樣你就可以改變這個值,而對這個包裝器的引用會和lambda代碼一起存儲氯夷。

2.4.2 捕捉可變變量

Java只允許捕捉final變量臣樱,而當你想捕捉可變變量的時候,可以使用兩種技巧:

  • 聲明一個單元素的數(shù)組腮考,其中存儲可變值
  • 創(chuàng)建一個包裝類的實例雇毫,其中存儲要改變的值的引用

這樣,當捕捉了一個可變變量var的時候踩蔚,它的值被作為Ref類的一個實例被存儲下來棚放,Ref變量是final的能輕易被捕捉,然后實際值存儲在其字段中馅闽,并且可以在lambda內(nèi)被修改飘蚯。

2.5 成員引用

2.5.1 基本概念

在上面的例子中,我們演示了 如何讓你把代碼塊作為參數(shù)傳遞給函數(shù)福也,但是如果要當做參數(shù)傳遞的代碼已經(jīng)被定義成了函數(shù)局骤,這時候就需要 把函數(shù)轉(zhuǎn)換成一個值,這種方式稱為 成員引用暴凑。

val getAge = Person :: age

它提供了簡明的語法峦甩,來創(chuàng)建一個 調(diào)用單個方法或者訪問單個屬性的函數(shù)值,雙冒號把 類名稱你要引用的成員(一個方法或者屬性)名稱 隔開现喳。

成員引用和調(diào)用該函數(shù)的lambda具有一樣的類型凯傲,所以可以互換使用:

people.maxBy(Person :: age)

2.5.2 引用頂層函數(shù)

除此之外,還可以引用頂層函數(shù)拿穴,這里我們省略了類名稱泣洞,直接以:開頭忧风,成員引用::salute被當作實參傳遞給庫函數(shù)run默色,它會調(diào)用相應(yīng)的函數(shù):


運行結(jié)果為:

2.5.3 存儲或者延期執(zhí)行創(chuàng)建類實例的動作

我們還可以使用 構(gòu)造方法引用 存儲或者延期執(zhí)行創(chuàng)建類實例的動作,構(gòu)造方法引用的形式是 在雙冒號后指定類的名稱

2.5.4 引用擴展函數(shù)

我們還可以以同樣的方式引用擴展函數(shù),這里我們定義一個擴展函數(shù)isAdult方法腿宰,選出年齡大于21的人呕诉。


運行結(jié)果為:

參考文章

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市吃度,隨后出現(xiàn)的幾起案子甩挫,更是在濱河造成了極大的恐慌,老刑警劉巖椿每,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件伊者,死亡現(xiàn)場離奇詭異,居然都是意外死亡间护,警方通過查閱死者的電腦和手機亦渗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來汁尺,“玉大人法精,你說我怎么就攤上這事〕胀唬” “怎么了搂蜓?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長辽装。 經(jīng)常有香客問我帮碰,道長,這世上最難降的妖魔是什么拾积? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任收毫,我火速辦了婚禮,結(jié)果婚禮上殷勘,老公的妹妹穿的比我還像新娘此再。我一直安慰自己,他們只是感情好玲销,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布输拇。 她就那樣靜靜地躺著,像睡著了一般贤斜。 火紅的嫁衣襯著肌膚如雪策吠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天瘩绒,我揣著相機與錄音猴抹,去河邊找鬼。 笑死锁荔,一個胖子當著我的面吹牛蟀给,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼跋理,長吁一口氣:“原來是場噩夢啊……” “哼择克!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起前普,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤肚邢,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后拭卿,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體骡湖,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年峻厚,在試婚紗的時候發(fā)現(xiàn)自己被綠了勺鸦。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡目木,死狀恐怖换途,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情刽射,我是刑警寧澤军拟,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站誓禁,受9級特大地震影響懈息,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜摹恰,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一辫继、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧俗慈,春花似錦姑宽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至酣溃,卻和暖如春瘦穆,著一層夾襖步出監(jiān)牢的瞬間脑溢,已是汗流浹背尊搬。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留瓢颅,地道東北人碘饼。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓熙兔,卻偏偏與公主長得像悲伶,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子黔姜,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

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