Lambda 表達(dá)式
1.lambda 表達(dá)式總是被大括號(hào)括著
2.其參數(shù)(如果有的話(huà))在->之前聲明(參數(shù)類(lèi)型可以省略)
- 函數(shù)體(如果存在的話(huà))在 ->后面。
4.在 Kotlin 中有一個(gè)約定,如果函數(shù)的最后一個(gè)參數(shù)是一個(gè)函數(shù),并且你傳遞一個(gè) lambda 表達(dá) 式作為相應(yīng)的參數(shù),你可以在圓括號(hào)之外指定它
一個(gè) lambda 表達(dá)式或匿名函數(shù)是一個(gè)“函數(shù)字面值”,即一個(gè)未聲明的函數(shù), 但立即做為表達(dá) 式傳遞??紤]下面的例子:
max(strings, { a, b -> a.length < b.length })
函數(shù) max 是一個(gè)高階函數(shù),換句話(huà)說(shuō)它接受一個(gè)函數(shù)作為第二個(gè)參數(shù)。 其第二個(gè)參數(shù)是一 個(gè)表達(dá)式,它本身是一個(gè)函數(shù),即函數(shù)字面值。寫(xiě)成函數(shù)的話(huà),它相當(dāng)于:
fun compare(a: String, b: String): Boolean = a.length < b.length
對(duì)于接受另一個(gè)函數(shù)作為參數(shù)的函數(shù),我們必須為該參數(shù)指定函數(shù)類(lèi)型。 例如上述函數(shù) max 定義如下:
fun <T> max(collection:Collection<T>, less: (T, T)-> Boolean): T? {
var max: T? = null
for (it in collection)
if(max == null || less(max, it))
max = it
return max
}
}
參數(shù) less 的類(lèi)型是 (T, T) -> Boolean ,即一個(gè)接受兩個(gè)類(lèi)型 T 的參數(shù)并返回一個(gè)布爾值 的函數(shù): 如果第一個(gè)參數(shù)小于第二個(gè)那么該函數(shù)返回 true。
匿名函數(shù)
上面提供的 lambda 表達(dá)式語(yǔ)法缺少的一個(gè)東西是指定函數(shù)的返回類(lèi)型的 能力。在大多數(shù)情 況下,這是不必要的。因?yàn)榉祷仡?lèi)型可以自動(dòng)推斷出來(lái)。然而,如果 確實(shí)需要顯式指定,可 以使用另一種語(yǔ)法: 匿名函數(shù) 。
fun(x: Int, y: Int): Int = x + y
匿名函數(shù)看起來(lái)非常像一個(gè)常規(guī)函數(shù)聲明,除了其名稱(chēng)省略了。其函數(shù)體 可以是表達(dá)式(如 上所示)或代碼塊。
匿名函數(shù)的返回類(lèi)型推斷機(jī)制與正常函數(shù)一樣:對(duì)于具有表達(dá)式函數(shù)體的匿名函數(shù)將自動(dòng) 推 斷返回類(lèi)型,而具有代碼塊函數(shù)體的返回類(lèi)型必須顯式 指定(或者已假定為Unit )。
請(qǐng)注意,匿名函數(shù)參數(shù)總是在括號(hào)內(nèi)傳遞。 允許將函數(shù) 留在圓括號(hào)外的簡(jiǎn)寫(xiě)語(yǔ)法僅適用于 lambda 表達(dá)式。
Lambda表達(dá)式和匿名函數(shù)之間的另一個(gè)區(qū)別是 非局部返回的行為。一個(gè)不帶標(biāo)簽的 return 語(yǔ)句 總是在用 fun 關(guān)鍵字聲明的函數(shù)中返回。這意味著 lambda 表達(dá)式中的 return 將從 包含它的函數(shù)返回,而匿名函數(shù)中的 return 將從匿名函數(shù)自身返回。
閉包
Lambda 表達(dá)式或者匿名函數(shù)(以及局部函數(shù)和對(duì)象表達(dá)式) 可以訪問(wèn)其 閉包 ,即在外部作 用域中聲明的變量。 與 Java 不同的是可以修改閉包中捕獲的變量:
var sum = 0
ints.filter
{ it > 0 }.forEach {
sum += it
}
print(sum)
高階函數(shù)
高階函數(shù)是將函數(shù)用作參數(shù)或返回值的函數(shù)。這種函數(shù)的一個(gè)很好的例子是 lock() ,它接 受一個(gè)鎖對(duì)象和一個(gè)函數(shù),獲取鎖,運(yùn)行函數(shù)并釋放鎖:
fun <T> lock(lock: Lock, body: () -> T): T{
lock.lock()
try {
return body()
}finally{
lock.unlock()
} }
讓我們來(lái)檢查上面的代碼: body 擁有函數(shù)類(lèi)型: () -> T , 所以它應(yīng)該是一個(gè)不帶參數(shù)并 且返回 T 類(lèi)型值的函數(shù)。 它在 try -代碼塊內(nèi)部調(diào)用、被 lock 保護(hù),其結(jié)果 由 lock() 函數(shù)返回。
如果我們想調(diào)用 lock() 函數(shù),我們可以把另一個(gè)函數(shù)傳給它作為參數(shù),通常會(huì)更方便的另一種方式是傳一個(gè) lambda 表達(dá)式:
fun toBeSynchronized() = sharedResource.operation()
val result = lock(lock, ::toBeSynchronized)
val result = lock(lock, { sharedResource.operation() })
內(nèi)聯(lián)函數(shù)
使用高階函數(shù)會(huì)帶來(lái)一些運(yùn)行時(shí)的效率損失:每一個(gè)函數(shù)都是一個(gè)對(duì)象,并且會(huì)捕獲一個(gè)閉 包。 即那些在函數(shù)體內(nèi)會(huì)訪問(wèn)到的變量。 內(nèi)存分配(對(duì)于函數(shù)對(duì)象和類(lèi))和虛擬調(diào)用會(huì)引入 運(yùn)行時(shí)間開(kāi)銷(xiāo)。
但是在許多情況下通過(guò)內(nèi)聯(lián)化 lambda 表達(dá)式可以消除這類(lèi)的開(kāi)銷(xiāo)。
下述函數(shù)是這種情況的 很好的例子。即lock()函數(shù)可以很容易地在調(diào)用處內(nèi)聯(lián)??紤]下面的情況:
lock(l) { foo() }
編譯器沒(méi)有為參數(shù)創(chuàng)建一個(gè)函數(shù)對(duì)象并生成一個(gè)調(diào)用。取而代之,編譯器可以生成以下代 碼:
l.lock() try {
foo()
} finally{
l.unlock()
}
這個(gè)不是我們從一開(kāi)始就想要的嗎?
為了讓編譯器這么做,我們需要使用 inline 修飾符標(biāo)記 lock() 函數(shù):
inline fun lock<T>(lock: Lock, body: () -> T): T
{ // …… }
inline 修飾符影響函數(shù)本身和傳給它的 lambda 表達(dá)式:所有這些都將內(nèi)聯(lián) 到調(diào)用處。
內(nèi)聯(lián)可能導(dǎo)致生成的代碼增加,但是如果我們使用得當(dāng)(不內(nèi)聯(lián)大函數(shù)),它將在 性能上有 所提升,尤其是在循環(huán)中的“超多態(tài)(megamorphic)”調(diào)用處。
Lambda表達(dá)式的返回
Kotlin 有函數(shù)字面量、局部函數(shù)和對(duì)象表達(dá)式。因此 Kotlin 的函數(shù)可以被嵌套。 標(biāo)簽限制的 return 允許我們從外層函數(shù)返回。 最重要的一個(gè)用途就是從 lambda 表達(dá)式中返回。
fun foo() {
ints.forEach {
if (it == 0)
return
print(it)
} }
//從 lambda 表達(dá)式中返回,
fun foo() {
ints.forEach lit@ {
if (it == 0)
return@lit
print(it) } }
//下劃線(xiàn)用于未使用的參數(shù)
map.forEach { _, value -> println("$value!") }
常用高階函數(shù)
1.forEach
提供了遍歷集合對(duì)象的功能,這里只查看IntArray類(lèi)的forEach方法實(shí)現(xiàn),源碼在_Arrays.kt文件中。
public inline fun IntArray.forEach(action: (Int) -> Unit): Unit {
for (element in this) action(element)
}
// 使用
val arr = intArrayOf(1, 2, 4, 6)
arr.forEach {
println(it)
}
2.let
它接收了調(diào)用者作為參數(shù)并且返回任意的類(lèi)型的lambda表達(dá)式,最后以自己為參數(shù)調(diào)用lambda表達(dá)式
代碼:
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
使用:
val arr = intArrayOf(1, 2, 4, 6)
arr.let {
var sum = 0
// 這個(gè)it就是arr對(duì)象
for (i in it) {
sum += i
}
println(sum)
}
3.map
map就是常用映射,函數(shù)其實(shí)就是一種映射關(guān)系,將輸入的參數(shù)映射成輸出的參數(shù)值。
public inline fun <R> IntArray.map(transform: (Int) -> R): List<R> {
return mapTo(ArrayList<R>(size), transform)
}
public inline fun <R, C : MutableCollection<in R>> IntArray.mapTo(destination: C, transform: (Int) -> R): C {
for (item in this)
destination.add(transform(item))
return destination
}
//使用
val arr = intArrayOf(1, 2, 4, 6)
val newArr = arr.map { (it * 2).toString() }
println(newArr)
4.flatMap
flatMap是一種支持二維集合映射的高階函數(shù)。
public inline fun <T, R> Iterable<T>.flatMap(transform: (T) -> Iterable<R>): List<R> {
return flatMapTo(ArrayList<R>(), transform)
}
public inline fun <T, R, C : MutableCollection<in R>> Iterable<T>.flatMapTo(destination: C, transform: (T) -> Iterable<R>): C {
for (element in this) {
val list = transform(element)
destination.addAll(list)
}
return destination
}
使用:
val arr = intArrayOf(1, 2, 4, 6)
val arr2 = intArrayOf(10, 39, 39, 18, 88)
var arr3 = intArrayOf(100, 200, 383, 198)
val newArr = arrayListOf(arr, arr2, arr3)
val flatArr = newArr.flatMap {
iterator -> iterator.map {
it.toString()
}
}
5.filter
對(duì)集合里的元素做過(guò)濾操作,只有那些符合要求的對(duì)象才需要用戶(hù)做處理
public inline fun IntArray.filter(predicate: (Int) -> Boolean): List<Int> {
return filterTo(ArrayList<Int>(), predicate)
}
public inline fun <C : MutableCollection<in Int>> IntArray.filterTo(destination: C, predicate: (Int) -> Boolean): C {
for (element in this) if (predicate(element)) destination.add(element)
return destination
}
使用:
val arr = intArrayOf(1, 2, 4, 6, 10, 39, 39, 18, 88)
val newArr = arr.filter { it % 2 == 0 }
println(newArr)
// 輸出結(jié)果
// [2, 4, 6, 10, 18, 88]
6.takeWhile
takeWhile和filter一樣都是過(guò)濾用的函數(shù),它的實(shí)現(xiàn)和filter不同地方在filter總是會(huì)遍歷當(dāng)前IntArray的所有元素,而takeWhile在第一次發(fā)現(xiàn)predict不滿(mǎn)足的時(shí)候就不再遍歷,后面的元素即使?jié)M足條件也不會(huì)加入到結(jié)果中。
public inline fun IntArray.takeWhile(predicate: (Int) -> Boolean): List<Int> {
val list = ArrayList<Int>()
for (item in this) {
if (!predicate(item))
break
list.add(item)
}
return list
}
使用:
val arr = intArrayOf(1, 2, 4, 6, 10, 39, 39, 18, 88)
val newArr = arr.takeWhile { it % 2 == 0 }
println(newArr)
// 輸出結(jié)果為空,因?yàn)榈谝粋€(gè)1不是偶數(shù),直接返回,沒(méi)有任何結(jié)果
// []
7.take/takeLast
take是從集合中取前幾個(gè)元素,takeLast是從集合中取后幾個(gè)元素。
public fun IntArray.take(n: Int): List<Int> {
require(n >= 0) { "Requested element count $n is less than zero." }
if (n == 0) return emptyList()
if (n >= size) return toList()
if (n == 1) return listOf(this[0])
var count = 0
val list = ArrayList<Int>(n)
for (item in this) {
if (count++ == n)
break
list.add(item)
}
return list
}
public fun IntArray.takeLast(n: Int): List<Int> {
require(n >= 0) { "Requested element count $n is less than zero." }
if (n == 0) return emptyList()
val size = size
if (n >= size) return toList()
if (n == 1) return listOf(this[size - 1])
val list = ArrayList<Int>(n)
for (index in size - n .. size - 1)
list.add(this[index])
return list
}
使用:
val arr = intArrayOf(1, 2, 4, 6, 10, 39, 39, 18, 88)
// [1, 2]
println(arr.take(2))
// [18, 88]
println(arr.takeLast(2))
8.fold
把集合里的所有元素結(jié)合成一個(gè)值。fold顧名思義就是折疊起來(lái),不過(guò)它會(huì)提供一個(gè)初始值。
public inline fun <R> IntArray.fold(initial: R, operation: (acc: R, Int) -> R): R {
var accumulator = initial
for (element in this)
accumulator = operation(accumulator, element)
return accumulator
}
使用:
val arr = intArrayOf(1, 2, 4, 6, 10, 39, 39, 18, 88)
arr.fold(2) { product, element ->
product * element
}
9.reduce
reduce也就是規(guī)約的意思,也是把多個(gè)值融合成一個(gè)值的操作,不過(guò)它并不會(huì)提供一個(gè)初始值。
public inline fun IntArray.reduce(operation: (acc: Int, Int) -> Int): Int {
if (isEmpty())
throw UnsupportedOperationException("Empty array can't be reduced.")
var accumulator = this[0]
for (index in 1..lastIndex) {
accumulator = operation(accumulator, this[index])
}
return accumulator
}
使用:
val arr = intArrayOf(1, 2, 4, 6, 10, 39, 39, 18, 88)
arr.reduce { product, element ->
product * element
}
10.apply
apply用于在lambda表達(dá)式里切換上下文的高階函數(shù)
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
使用:
class DbConfig {
var url: String = ""
var username: String = ""
var password: String = ""
override fun toString(): String {
return "url = $url, username = $username, password = $password"
}
}
class DbConnection {
fun config(conf: DbConfig) {
println(conf)
}
}
fun main(args: Array<String>) {
val conn = DbConnection()
conn.config(DbConfig().apply {
url = "mysql://127.0.0.1:3306/hello"
username = "root"
password = "123456"
})
}
11.with
ith可以讓用戶(hù)省略點(diǎn)號(hào)之前的對(duì)象引用,with內(nèi)部的所有操作都是針對(duì)with對(duì)象
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return receiver.block()
}
使用:
class MyLogger {
var tag: String = "TAG"
fun e(msg: String) {
println("$tag $i")
}
fun tag(tagStr: String) {
tag = tagStr
}
}
fun main(args: Array<String>) {
val logger = MyLogger()
with(logger) {
tag("Kotlin")
e("It is a good language")
}
}
12.use
針對(duì)那些實(shí)現(xiàn)了Closable接口的對(duì)象的擴(kuò)展方法,也就是大部分的IO操作相關(guān)類(lèi)會(huì)有這個(gè)擴(kuò)展高階方法,查看它的源代碼在Closable.kt源文件中。
public inline fun <T : Closeable?, R> T.use(block: (T) -> R): R {
var exception: Throwable? = null
try {
return block(this)
} catch (e: Throwable) {
exception = e
throw e
} finally {
when {
apiVersionIsAtLeast(1, 1, 0) -> this.closeFinally(exception)
this == null -> {}
exception == null -> close()
else ->
try {
close()
} catch (closeException: Throwable) {
// cause.addSuppressed(closeException) // ignored here
}
}
}
}
使用:
val file = File("test.txt")
val bufferReader = BufferedReader(FileReader(file))
bufferReader.use {
it.readLine()
}