Kotlin 函數(shù)都是頭等的许赃,這意味著它們可以存儲在變量與數(shù)據(jù)結(jié)構(gòu)中、作為參數(shù)傳遞給其他高階函數(shù)以及從其 他高階函數(shù)返回洽胶。可以像操作任何其他非函數(shù)值?樣操作函數(shù)裆馒。
一姊氓、高階函數(shù)
高階函數(shù)是將函數(shù)用作參數(shù)或返回值的函數(shù)。
二喷好、函數(shù)類型
Kotlin 使用類似 (Int) -> String 的?系列函數(shù)類型來處理函數(shù)的聲明:val onClick: () -> Unit = ……翔横。
這些類型具有與函數(shù)簽名相對應(yīng)的特殊表示法,即它們的參數(shù)和返回值:
- 所有函數(shù)類型都有?個圓括號括起來的參數(shù)類型列表以及?個返回類型:(A, B) -> C 表?接受類型分 別為 A 與 B 兩個參數(shù)并返回?個 C 類型值的函數(shù)類型梗搅。參數(shù)類型列表可以為空禾唁,如 () -> A 。Unit 返 回類型不可省略无切。
- 函數(shù)類型可以有?個額外的接收者類型荡短,它在表示法中的點之前指定:類型 A.(B) -> C 表示可以在 A 的接收者對象上以?個B類型參數(shù)來調(diào)用并返回?個 C 類型值的函數(shù)。帶有接收者的函數(shù)字面值通常與這些類型?起使用哆键。
- 掛起函數(shù)屬于特殊種類的函數(shù)類型掘托,它的表示法中有?個 suspend 修飾符 ,例如 suspend () -> Unit 或者 suspend A.(B) -> C 籍嘹。
函數(shù)類型表示法可以選擇性地包含函數(shù)的參數(shù)名:(x: Int, y: Int) -> Point 闪盔。這些名稱可用于表明參數(shù)的含義弯院。
- 如需將函數(shù)類型指定為可空,請使?圓括號:((Int, Int) -> Int)?泪掀。
- 函數(shù)類型可以使?圓括號進?接合:(Int) -> ((Int) -> Unit)
- 箭頭表是法是右結(jié)合的听绳,(Int) -> (Int) -> Unit 與前述?例等價,但不等于 ((Int) -> (Int)) -> Unit异赫。
還可以通過使?類型別名給函數(shù)類型起?個別稱:
typealias ClickHandler = (Button, ClickEvent) -> Unit
1.函數(shù)類型實例化
- 使用函數(shù)字面值的代碼塊椅挣,采用以下形式之?:
- lambda 表達式: { a, b -> a + b }
- 匿名函數(shù): fun(s: String): Int { return s.toIntOrNull() ?: 0 }
- 使用已有聲明的可調(diào)用引用:
- 頂層、局部塔拳、成員贴妻、擴展函數(shù):::isOdd 、String::toInt
- 頂層蝙斜、成員名惩、擴展屬性:List<Int>::size
- 構(gòu)造函數(shù):::Regex
- 使?實現(xiàn)函數(shù)類型接?的?定義類的實例:
class IntTransformer : (Int) -> Int {
override operator fun invoke(x: Int): Int = TODO()
}
val intFunction: (Int) -> Int = IntTransformer()
帶與不帶接收者的函數(shù)類型非字面值可以互換,其中接收者可以替代第?個參數(shù)孕荠,反之亦然娩鹉。例如,(A, B) -> C 類型的值可以傳給或賦值給期待 A.(B) -> C 的地方稚伍,反之亦然
val repeatFun: String.(Int) -> String = { times -> this.repeat(times) }
val twoParameters: (String, Int) -> String = repeatFun
fun runTransformation(f: (String, Int) -> String): String {
return f("hello", 3)
}
val result = runTransformation(repeatFun)
2.函數(shù)類型實例調(diào)用
函數(shù)類型的值可以通過其 invoke(……) 操作符調(diào)用:f.invoke(x) 或者直接 f(x) 弯予。
如果該值具有接收者類型,那么應(yīng)該將接收者對象作為第?個參數(shù)傳遞个曙。調(diào)用帶有接收者的函數(shù)類型值的另?個方式是在其前面加上接收者對象锈嫩,就好比該值是?個擴展函數(shù):1.foo(2)
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)) // 類擴展調(diào)?
3.內(nèi)聯(lián)函數(shù)
用內(nèi)聯(lián)函數(shù)可以為高階函數(shù)提供靈活的控制流。
三垦搬、Lambda表達式和匿名函數(shù)
lambda 表達式與匿名函數(shù)是“函數(shù)字面值”呼寸,即未聲明的函數(shù)
1.Lambda 表達式語法
lambda 表達式總是括在花括號中,完整語法形式的參數(shù)聲明放在花括號內(nèi)猴贰,并有可選的類型標(biāo)注对雪,函數(shù)體跟在 ?個 -> 符號之后。如果推斷出的該 lambda 的返回類型不是 Unit米绕,那么該 lambda 主體中的最后?個(或 可能是單個)表達式會視為返回值瑟捣。
val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }
val sum = { x: Int, y: Int -> x + y }//可選標(biāo)注都留下
2.傳遞末尾的 lambda 表達式
在 Kotlin 中有?個約定:如果函數(shù)的最后?個參數(shù)是函數(shù),那么作為相應(yīng)參數(shù)傳入的 lambda 表達式可以放在圓括號之外,這種語法也稱為拖尾 lambda 表達式栅干。
val product = items.fold(1) { acc, e -> acc * e }
如果該 lambda 表達式是調(diào)用時唯?的參數(shù)迈套,那么圓括號可以完全省略:
run { println("...") }
3.it:單個參數(shù)的隱式名稱
如果編譯器自己可以識別出簽名,也可以不用聲明唯?的參數(shù)并忽略 -> 碱鳞。該參數(shù)會隱式聲明為 it:
ints.filter { it > 0 } // 這個字?值是“(it: Int) -> Boolean”類型的
4.從 lambda 表達式中返回?個值
使用返回標(biāo)簽語法從 lambda 顯式返回?個值桑李。否則,將隱式返回最后?個表達式的值。
ints.filter {
val shouldFilter = it > 0
shouldFilter
}
ints.filter {
val shouldFilter = it > 0
return@filter shouldFilter
}
搭配圓括號可外傳遞函數(shù)類型參數(shù)lambda表達式,可支持鏈?zhǔn)骄幊蹋↙INQ)語法:
strings.filter { it.length == 5 }.sortedBy { it }.map { it.toUpperCase() }
5.下劃線用于未使用的變量
如果 lambda 表達式的參數(shù)未使用芙扎,那么可以用下劃線取代其名稱:
map.forEach { _, value -> println("$value!") }
6.在 lambda 表達式中解構(gòu)
你可以對 lambda 表達式參數(shù)使?解構(gòu)聲明語法。如果 lambda 表達式具有 Pair 類型(或者 Map.Entry 或任何其他具有相應(yīng) componentN 函數(shù)的類型)的參數(shù)填大,那么可以通過將它們放在括號中來引?多個新參數(shù) 來取代單個新參數(shù):
map.mapValues { entry -> "${entry.value}!" }
map.mapValues { (key, value) -> "$value!" }
7.匿名函數(shù)
需要顯示指定函數(shù)返回類型可以使用匿名函數(shù)戒洼。看起來非常像?個常規(guī)函數(shù)聲明允华,除了其名稱省略了圈浇。其函數(shù)體可以是表達式或代碼塊:
//表達式
fun(x: Int, y: Int): Int = x + y
//代碼塊
fun(x: Int, y: Int): Int {
return x + y
}
匿名函數(shù)的返回類型推斷機制與正常函數(shù)?樣:對于具有表達式函數(shù)體的匿名函數(shù)將?動推斷返回類型,?具 有代碼塊函數(shù)體的返回類型必須顯式指定(或者已假定為 Unit )靴寂。
匿名函數(shù)和Lambda的區(qū)別:
- 匿名函數(shù)參數(shù)總是在括號內(nèi)傳遞磷蜀。允許將函數(shù)留在圓括號外的簡寫語法僅適?于 lambda 表達式。
- Lambda表達式與匿名函數(shù)之間的另?個區(qū)別是非局部返回的行為百炬。?個不帶標(biāo)簽的 return 語句總是在用fun 關(guān)鍵字聲明的函數(shù)中返回褐隆。這意味著 lambda 表達式中的 return 將從包含它的函數(shù)返回,而匿名函數(shù)中 的 return 將從匿名函數(shù)自身返回剖踊。
8.閉包
Lambda 表達式或者匿名函數(shù)(以及局部函數(shù)和對象表達式)可以訪問其 閉包 庶弃,即在外部作?域中聲明的變 量。在 lambda 表達式中可以修改閉包中捕獲的變量:
var sum = 0
ints.filter { it > 0 }.forEach {
sum += it
}
print (sum)
9.帶有接收者的函數(shù)字面值
帶有接收者的函數(shù)類型德澈,例如 A.(B) -> C 歇攻,可以用特殊形式的函數(shù)字面值實例化—— 帶有接收者的函數(shù)字面值。
在這樣的函數(shù)字?值內(nèi)部梆造,傳給調(diào)?的接收者對象成為隱式的this缴守,以便訪問接收者對象的成員而無需任何額外的限定符,亦可使用 this 表達式 訪問接收者對象镇辉。
val sum: Int.(Int) -> Int = { other -> plus(other) }
匿名函數(shù)語法允許你直接指定函數(shù)字?值的接收者類型
val sum = fun Int.(other: Int): Int = this + other