高阶函数的基本概念

高阶函数是将函数用作参数或返回值的函数。类似于数学中的高阶函数f(g(x))。看看官方的例子:

fun <T> lock(lock: Lock, body: () -> T): T {
    lock.lock()
    try {
        return body()
    }
    finally {
        lock.unlock()
    }
}

上面的代码中:body 拥有函数类型:() -> T, 所以它应该是一个不带参数并且返回T类型值的函数。 它在try-代码块内部调用、被lock保护,其结果由lock()函数返回。

高阶函数通常与lambda表达式一起使用:

val result = lock(lock, { sharedResource.operation() })

Kotlin中有一个约定,如果函数的最后一个参数是一个函数,并且你传递一个lambda表达式作为相应的参数,你可以在圆括号之外指定它:

val result = lock (lock) {
    sharedResource.operation()
}

常用高阶函数

这里收集了Kotlin标准库里面常用的高阶函数的定义和用法,以备日后查询:

1. apply

/**
 * Calls the specified function [block] with `this` value as its receiver and returns `this` value.
 */
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }

调用某对象的apply函数,在函数范围内,可以通过this调用该对象的任意方法,并返回该对象,this可以不显示的写处理。例如:

//生成一个TextView实例,并设置相应的属性后返回给textView
val textView = TextView(this).apply {
      //this.text = "Hello World"
      text = "Hello World"
      setTextColor(Color.BLUE)
}

//等同于
val textView = TextView(this)
textView.text = "Hello World"
textView.setTextColor(Color.BLUE)

2. let

/**
 * Calls the specified function [block] with `this` value as its argument and returns its result.
 */
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R = block(this)

let函数默认当前这个对象作为闭包的it参数,返回值是函数里面最后一行,或者指定return。例如上述的例子:

//let函数默认返回最后一行
val textView = TextView(this)
val text: String = textView.let {
      it.text = "Hello World"
      it.setTextColor(Color.BLUE)
      it.text.toString()
}
print(text)//结果为Hello World

3.also

/**
 * Calls the specified function [block] with `this` value as its argument and returns `this` value.
 */
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.also(block: (T) -> Unit): T { block(this); return this }

also可以看作是applylet的结合:默认当前这个对象作为闭包的it参数,最后返回自己,例如:

//生成一个TextView实例,并设置相应的属性后将it返回给textView
val textView = TextView(this).also {
     it.text = "Hello World"
     it.setTextColor(Color.BLUE)
}

4.run

run有两种,一种是applylet的另一种结合体:在函数范围内,可以任意调用该对象的任意方法,返回函数里面最后一行,或者指定return

/**
 * Calls the specified function [block] with `this` value as its receiver and returns its result.
 */
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R = block()

例子:

//跟let相比,不需要it作为参数了
val textView = TextView(this)
val text: String = textView.run {
//this.text = "Hello World"
      text = "Hello World"
      setTextColor(Color.BLUE)
      text.toString()
}
print(text)//结果为Hello World

另一种run是:提供() -> R 的转换,并返回最后一行,或者指定return

/**
 * Calls the specified function [block] and returns its result.
 */
@kotlin.internal.InlineOnly
public inline fun <R> run(block: () -> R): R = block()

例如:

val textView = TextView(this).apply {
      val text: String = run {
//this.text = "Hello World"
        text = "Hello World"
        setTextColor(Color.BLUE)
        text.toString()
      }
      print(text)//结果为Hello World
}

5.with

/**
 * Calls the specified function [block] with the given [receiver] as its receiver and returns its result.
 */
@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()

with函数接收两个参数,一个是调用者,第二个为一个函数,在函数库内可以通过this指代调用者(第一个参数)来调用,返回值为函数块的最后一行或指定return。例如:

val textView = TextView(this)
    val text: String = with(textView) {
      //this.text = "Hello World"
      text = "Hello World"
      setTextColor(Color.BLUE)
      text.toString()
}
print(text)//结果为Hello World

 

6.repeat

/**
 * Executes the given function [action] specified number of [times].
 *
 * A zero-based index of current iteration is passed as a parameter to [action].
 */
@kotlin.internal.InlineOnly
public inline fun repeat(times: Int, action: (Int) -> Unit) {
    for (index in 0..times - 1) {
        action(index)
    }
}

这个没什么好解释的,就是重复某操作指定次数,例如

repeat(10) {print(text)}//打印10次Hello World

7.takeIf

/**
 * Returns `this` value if it satisfies the given [predicate] or `null`, if it doesn't.
 */
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? = if (predicate(this)) this else null

takeIf接收一个字面值为predicate的函数(T) -> Boolean,这个函数的参数为T (即takeIf 的调用者),返回Boolean

takeIf的作用就是字面意思,如果(if)predicate为真,则采用(take)调用者。例如:

val flag = 1
var result = "Hello World".takeIf {
      flag == 1
}

println(result)//因为flag == 1,所以这里可以打印出Hello World
    
//等同于
var result: String? = null
if (flag == 1) {
  result = "Hello World"
}

实际上个人觉得如果是新创建对象还是使用if的写法更妥当,因为通过反编译takeIf 可以看到,"Hello World"这个对象已经被创建,如果predicate不成立,这个对象没有被赋予result就被浪费了。而对于已经存在的旧对象做判断,则可以使用takeIf

8.takeUnless

/**
 * Returns `this` value if it _does not_ satisfy the given [predicate] or `null`, if it does.
 */
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? = if (!predicate(this)) this else null

takeIf 相反,predicate不成立,才采用调用者,例如:

    val flag = 1
    var result = "Hello World".takeUnless {
      flag == 1
    }

    println(result)//因为flag == 1,所以这里会抛出空指针

9.lazy

public fun <T> lazy(initializer: () -> T): Lazy<T>
public fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T>
public fun <T> lazy(lock: Any?, initializer: () -> T): Lazy<T>

这是一个提供延时加载能力的代理属性,接受一个lambda并返回一个Lazy <T>实例的函数,返回的实例可以作为实现延迟属性的委托: 第一次调用 get()会执行已传递给lazy()lambda表达式并记录结果, 后续调用 get()只是返回记录的结果。例子:

val lazyValue: String by lazy {
    println("computed!")
    "Hello"
}

//第一次访问lazyValue,会打印"computed!"和"Hello"
println(lazyValue)

//第二次访问只打印结果"Hello"
println(lazyValue)

默认情况下,对于lazy属性的求值是同步锁的(synchronized):该值只在一个线程中计算,并且所有线程会看到相同的值。如果初始化委托的同步锁不是必需的,这样多个线程可以同时执行,那么可以将LazyThreadSafetyMode.PUBLICATION作为参数传递给lazy()函数。 而如果你确定初始化将总是发生在单个线程,那么你可以使用LazyThreadSafetyMode.NONE模式, 它不会有任何线程安全的保证和相关的开销。

10.use

public inline fun <T : Closeable?, R> T.use(block: (T) -> R): R {
    var closed = false
    try {
        return block(this)
    } catch (e: Exception) {
        closed = true
        try {
            this?.close()
        } catch (closeException: Exception) {
        }
        throw e
    } finally {
        if (!closed) {
            this?.close()
        }
    }
}

用来简化Closeable的操作,例如closetry/catch,使用统一的模板:

BufferedReader(FileReader("test.txt")).use {
    var line: String?
    while (true) {
        line = it.readLine() ?: break
        println(line)
    }
}

11.filter

过滤所有符合给定函数条件的元素。

val list = listOf(1,2,3,4,5,6)
val oddList = list.filter { it % 2 == 1 }
println(oddList)//结果为[1,3,5]

//filter操作等同于
for (i in list) {
    if (i % 2 == 1) {
        oddList.add(i)
    }
}

12.map

返回一个每一个元素根据给定的函数转换所组成的List。

val list = listOf(1,2,3,4,5,6)
val doubleList = list.map { it * 2 }
println(doubleList)//结果为[2,4,6,8,10,12]

//map操作等同于
for (i in list) {
    doubleList.add(i * 2)
}

13.forEach

遍历所有元素,并执行给定的操作。

list.forEach { println(it) }
//等同于
for (i in list) {
    println(i)
}