[Kotlin Tutorials 6] Lambda表達(dá)式和高階函數(shù)

Lambda表達(dá)式和高階函數(shù)

本文收錄于: https://github.com/mengdd/KotlinTutorials

在Kotlin中函數(shù)是第一公民, 不像Java一樣, 函數(shù)一定需要寫在某個類里面, Kotlin中的函數(shù)也可以直接寫在文件里.

Lambda表達(dá)式并不是什么新東西, Java 8就有了.
它的存在主要是為了讓代碼更加簡潔.

高階函數(shù)呢, 基本概念也很簡單, 就是函數(shù), 同樣也可以像其他類型的對象一樣, 作為一個函數(shù)的參數(shù)和返回值.

Lambda表達(dá)式

lambda表達(dá)式和匿名函數(shù)都是函數(shù)字面值(function literals), 它們沒有提前聲明, 直接作為表達(dá)式傳入.

Lambda表達(dá)式的語法

lambda表達(dá)式的構(gòu)成:

{ 參數(shù)列表 -> 函數(shù)體 }

其中參數(shù)類型是可以省略的.
如果函數(shù)體的返回值不是Unit, 那么最后一個表達(dá)式會被當(dāng)做返回值.

舉例: setOnClickListener

對Lambda表達(dá)式的應(yīng)用很大程度上要感謝現(xiàn)代化的IDE, 比如一個簡單的給button設(shè)置listener的代碼,

最開始, 作為一個把Kotlin當(dāng)Java 7用的人, 可能會寫成這樣:

button.setOnClickListener(object : View.OnClickListener{
    override fun onClick(v: View?) {
    }
})

這里利用了object關(guān)鍵字, 聲明了一個匿名類的對象.

但是IDE看到這個代碼就不那么開心了, 出現(xiàn)了一條黃色的下劃線, Alt + Enter, "Convert to lambda", 再Enter確認(rèn), 就變成了這樣:

button.setOnClickListener { }

為什么可以這樣干呢, 這是因為ViewOnClickListener接口是一種特殊類型的接口: 只有一個方法.

這種只擁有一個方法的接口被稱作是函數(shù)式接口(functional interface), 也被叫做單抽象方法類型, 即SAM, (single abstract method).

PS: 一個錯誤的示范:
如果你不幸一開始把代碼寫成了這樣:

button.setOnClickListener{object : View.OnClickListener {
    override fun onClick(v: View?) {
    }
}}

注意這里與上面例子的區(qū)別僅僅是把小括號()變成了大括號{}.
程序沒有報錯, 但按鈕點擊的時候應(yīng)該是執(zhí)行不到你想要的代碼了.

IDE仍然會提示你簡化, 簡化后變成了這樣:

button.setOnClickListener{ View.OnClickListener { } }

也就是說每次按鈕點擊, 你做的事情都是創(chuàng)建了一個匿名對象.

為什么會犯這種錯誤, 而IDE又不提示呢, 這是因為lambda的慣例允許這樣的寫法, 這么寫雖然邏輯上有問題, 但是語法上是沒有錯誤的.
下面請看都有什么慣例呢.

lambda慣例

  • 如果函數(shù)的最后一個參數(shù)接收函數(shù), 傳入的lambda表達(dá)式可以寫在圓括號外面. (這種語法稱為trailing lambda).
    在這種情況下, 如果lambda是唯一的參數(shù), 那么圓括號可以省略. (上面的錯誤例子就是因為符合了這個慣例, 所以語法上沒有錯誤).
  • 如果lambda只有一個參數(shù), 可以不聲明直接用, 隱式名字it.
  • lambda表達(dá)式的返回值: 可以顯式return, 如果不顯式返回, 默認(rèn)返回最后一個表達(dá)式的值.
  • 沒有用到的參數(shù)可以用下劃線_表示.

匿名函數(shù)

lambda表達(dá)式并不能顯式指定返回類型, 大多數(shù)情況下可能用不上這一點, 因為往往返回類型都是可以被自動推斷出來的.

但是如果你真的需要顯式指定, 你可以用匿名函數(shù).

fun(x: Int, y: Int): Int = x + y

匿名函數(shù)看起來和普通的函數(shù)聲明很像, 只是它沒有名字.

和普通函數(shù)不同的是, 如果參數(shù)類型可以從上下文推斷出來, 那么可以省略不寫參數(shù)類型:

ints.filter(fun(item) = item > 0)

匿名函數(shù)的返回類型推斷和正常函數(shù)一樣.

匿名函數(shù)與Lambda的區(qū)別:

  • 匿名函數(shù)的參數(shù)永遠(yuǎn)是在圓括號內(nèi)傳遞的, 把函數(shù)參數(shù)放在圓括號外的簡化語法只適用于lambda表達(dá)式.
  • 不帶標(biāo)簽的return語句: 在lambda中, 返回外層的函數(shù), 在匿名函數(shù)中, 返回本函數(shù).

高階函數(shù)

高階函數(shù)(Higher-Order Functions): 把函數(shù)作為參數(shù)或返回值的函數(shù).

函數(shù)類型

函數(shù)類型(Function types): 函數(shù)根據(jù)參數(shù)和返回值, 可以歸類到一個函數(shù)類型.

函數(shù)類型的寫法

函數(shù)類型的基本寫法: 括號中的參數(shù)列表和一個返回值. 比如(A, B) -> C就是一個函數(shù)類型, 這種類型的函數(shù)接受A和B兩種類型的參數(shù), 返回一個類型C的返回值.

  • 參數(shù)列表可以為空, 比如() -> A.
  • 返回值為Unit時不能省略.
  • 函數(shù)類型還可以寫接收器(receiver)類型: A.(B) -> C. 表示這種函數(shù)是在A類型上調(diào)用的.
  • suspend關(guān)鍵字表示一種特殊的函數(shù)類型, 所以如果有, suspend修飾符也要出現(xiàn). 比如suspend () -> Unit.
  • 也可以加上參數(shù)名, 來表達(dá)參數(shù)的意義. 如(x: Int, y: Int) -> Point.

幾點比較特殊的:

  • 函數(shù)類型也有可空類型, 加括號和?. 比如((Int, Int) -> Int)?.
  • 函數(shù)類型可以是高階的, 用括號組合, 如: (Int) -> ((Int) -> Unit).
  • 箭頭是按照右結(jié)合的原則, 即運算按照從右向左的順序, 所以(Int) -> (Int) -> Unit(Int) -> ((Int) -> Unit)是一個意思.

可以給函數(shù)類型一個類型別名, 比如:

typealias ClickHandler = (Button, ClickEvent) -> Unit

實例化函數(shù)類型

實例化函數(shù)類型有好幾種方法:

  • 用函數(shù)字面值(function literal)的代碼塊: lambda表達(dá)式, 匿名函數(shù).
  • 使用已有的聲明引用: 函數(shù), 屬性, 構(gòu)造函數(shù). ::的用法參見Callable Reference.
  • 函數(shù)類型還可以當(dāng)做接口被類實現(xiàn), 那么創(chuàng)建這個類的實例就是創(chuàng)建了函數(shù)類型的實例.

調(diào)用函數(shù)類型的實例

函數(shù)類型聲明了, 也實例化了, 怎么調(diào)用呢?
可以用invoke操作符, 也可以直接用名稱調(diào)用.

如果有接受者類型(receiver), 那么接受者對象需要作為第一個參數(shù).
另一種方式也可以將接受者對象放在點(.)前面, 像擴(kuò)展函數(shù)一樣調(diào)用.

val stringPlus: (String, String) -> String = String::plus
val intPlus: Int.(Int) -> Int = Int::plus

println(stringPlus.invoke("<-", "->"))
println(stringPlus("Hello, ", "world!")) 

println(intPlus.invoke(1, 1))
println(intPlus(1, 2))
println(2.intPlus(3)) // extension-like call

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容