Kotlin 高阶函数学习指南

0 阅读12分钟

这是一份纯 Kotlin 视角的高阶函数学习文档,适合系统入门和反复查阅。

内容覆盖:

  • 高阶函数的定义
  • 函数类型的写法
  • Lambda 表达式
  • 匿名函数
  • 函数引用
  • 闭包
  • 内联函数
  • 作用域函数
  • 集合与序列中的高阶函数
  • 协程与 Flow 中常见的高阶函数
  • 常见误区与练习

1. 什么是高阶函数

高阶函数指的是满足以下任意条件的函数:

  • 接收函数作为参数
  • 返回一个函数

例如:

fun calculate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
    return operation(a, b)
}
​
fun main() {
    val result = calculate(3, 4) { x, y -> x + y }
    println(result) // 7
}

这里的 calculate 就是高阶函数,因为它接收了一个函数参数 operation


2. 为什么要学高阶函数

高阶函数是 Kotlin 非常核心的能力之一,因为它能让代码:

  • 更抽象
  • 更简洁
  • 更容易复用
  • 更接近“表达意图”

例如,遍历列表时,你不一定需要自己写 for 循环:

val result = listOf(1, 2, 3, 4)
    .filter { it > 2 }
    .map { it * 10 }

这里的重点不是“怎么手动一项项处理”,而是“我要过滤”和“我要转换”。


3. 函数类型基础

在 Kotlin 中,函数也有类型。

3.1 最常见的函数类型

(Int, Int) -> Int

含义:

  • 接收两个 Int
  • 返回一个 Int

再例如:

() -> Unit

含义:

  • 不接收参数
  • 不返回结果

再例如:

(String) -> Boolean

含义:

  • 接收一个 String
  • 返回一个 Boolean

3.2 声明函数类型变量

val add: (Int, Int) -> Int = { a, b -> a + b }

3.3 作为函数参数

fun execute(action: () -> Unit) {
    action()
}

3.4 作为函数返回值

fun createPrinter(): (String) -> Unit {
    return { text -> println(text) }
}

4. Lambda 表达式

Lambda 是 Kotlin 中最常见的函数字面量写法。

基本结构:

{ 参数 -> 函数体 }

4.1 最简单的例子

val square: (Int) -> Int = { x -> x * x }

4.2 多参数 Lambda

val add: (Int, Int) -> Int = { a, b -> a + b }

4.3 没有参数的 Lambda

val sayHello: () -> Unit = { println("Hello") }

4.4 最后一行是返回值

val multiply: (Int, Int) -> Int = { a, b ->
    println("计算中")
    a * b
}

这里最后一行 a * b 就是返回值。


5. it 的含义

当 lambda 只有一个参数时,可以省略参数名,直接使用默认参数名 it

val lengths = listOf("a", "ab", "abc").map { it.length }

等价于:

val lengths = listOf("a", "ab", "abc").map { text -> text.length }

所以:

  • it 只是默认参数名
  • 不是特殊关键字语义
  • 只是为了简化书写

6. 尾随 Lambda

如果函数最后一个参数是 lambda,就可以把它写到括号外面。

例如:

list.filter({ it > 0 })

通常会写成:

list.filter { it > 0 }

这是 Kotlin 非常常见的风格。

再比如:

run({
    println("running")
})

可以写成:

run {
    println("running")
}

7. 高阶函数的两种核心形态

7.1 接收函数参数

fun repeatAction(times: Int, action: (Int) -> Unit) {
    for (i in 0 until times) {
        action(i)
    }
}

调用:

repeatAction(3) {
    println("第 $it 次")
}

7.2 返回函数

fun createAdder(base: Int): (Int) -> Int {
    return { value -> base + value }
}
​
fun main() {
    val add10 = createAdder(10)
    println(add10(5)) // 15
}

8. 匿名函数

除了 lambda,还可以使用匿名函数。

val add = fun(a: Int, b: Int): Int {
    return a + b
}

Lambda 和匿名函数都能表示“没有名字的函数”,但匿名函数更接近普通函数语法。

8.1 什么时候用匿名函数

适合:

  • 逻辑较复杂
  • 想写显式 return
  • 不想混淆 lambda 返回规则

9. 函数引用

如果一个函数已经存在,可以直接把它的引用传出去。

9.1 普通函数引用

fun isEven(x: Int): Boolean = x % 2 == 0val result = listOf(1, 2, 3, 4).filter(::isEven)

9.2 成员引用

val lengths = listOf("a", "ab", "abc").map(String::length)

9.3 构造函数引用

class User(val name: String)
​
val creator: (String) -> User = ::User

函数引用的好处:

  • 更简洁
  • 可复用已有函数
  • 某些场景下比 lambda 更清晰

10. 闭包 Closure

Lambda 可以访问它定义时所在作用域中的变量,这种能力叫闭包。

fun main() {
    var sum = 0
    listOf(1, 2, 3).forEach {
        sum += it
    }
    println(sum) // 6
}

这里 lambda 修改了外部变量 sum

闭包很强大,但也要注意:

  • 如果频繁修改外部可变状态,代码可能会变得难以维护

11. 常见集合高阶函数

集合是高阶函数最经典的应用场景。

11.1 forEach

对每个元素执行操作。

listOf(1, 2, 3).forEach {
    println(it)
}

11.2 map

把每个元素转换成新元素。

val doubled = listOf(1, 2, 3).map { it * 2 }

11.3 mapIndexed

转换时拿到下标。

val result = listOf("a", "b").mapIndexed { index, value ->
    "$index:$value"
}

11.4 filter

保留符合条件的元素。

val result = listOf(1, 2, 3, 4).filter { it % 2 == 0 }

11.5 filterNot

去掉符合条件的元素。

val result = listOf(1, 2, 3, 4).filterNot { it % 2 == 0 }

11.6 any

判断是否至少有一个元素满足条件。

val hasEven = listOf(1, 3, 4).any { it % 2 == 0 }

11.7 all

判断是否全部满足条件。

val allPositive = listOf(1, 2, 3).all { it > 0 }

11.8 none

判断是否没有元素满足条件。

val noneNegative = listOf(1, 2, 3).none { it < 0 }

11.9 count

统计满足条件的元素数量。

val count = listOf(1, 2, 3, 4, 5).count { it % 2 == 0 }
println(count) // 2

11.9 firstOrNull

获取第一个符合条件的元素,找不到返回 null

val firstEven = listOf(1, 3, 4, 6).firstOrNull { it % 2 == 0 }

11.10 find

findfirstOrNull 很接近。

val firstBig = listOf(1, 10, 20).find { it > 5 }

11.11 associateBy

把列表变成 Map。

data class User(val id: Int, val name: String)

val users = listOf(User(1, "Tom"), User(2, "Jack"))
val userMap = users.associateBy { it.id }

11.12 groupBy

按某个规则分组。

val grouped = listOf("apple", "ape", "banana").groupBy { it.first() }

11.13 fold

累积结果。

val sum = listOf(1, 2, 3, 4).fold(0) { acc, item ->
    acc + item
}

11.14 reduce

fold 类似,但没有初始值。

val sum = listOf(1, 2, 3).reduce { acc, item ->
    acc + item
}

11.15 flatMap

映射后拍平。

val result = listOf("a,b", "c,d").flatMap { it.split(",") }

11.16 distinct

去重,保留第一次出现的元素顺序。

val result = listOf(1, 2, 2, 3, 1).distinct()
println(result) // [1, 2, 3]

11.17 distinctBy

按某个字段去重。

data class User(val id: Int, val name: String)

val users = listOf(
    User(1, "Tom"),
    User(1, "Tom Duplicate"),
    User(2, "Jack")
)

val result = users.distinctBy { it.id }
println(result)

11.18 sortedByDescending

按某个规则降序排序。

val result = listOf(1, 5, 2, 4).sortedByDescending { it }
println(result) // [5, 4, 2, 1]

11.19 partition

按条件一分为二,返回一个 Pair

val (even, odd) = listOf(1, 2, 3, 4, 5).partition { it % 2 == 0 }
println(even) // [2, 4]
println(odd)  // [1, 3, 5]

11.20 zip

把两个集合按下标配对。

val names = listOf("Tom", "Jack")
val ages = listOf(18, 20)

val result = names.zip(ages)
println(result) // [(Tom, 18), (Jack, 20)]

11.21 associate

把集合转换成 Map,键值都由你自己决定。

val result = listOf("a", "bb", "ccc").associate { it to it.length }
println(result) // {a=1, bb=2, ccc=3}

11.22 sumOf

按规则求和。

data class Product(val name: String, val price: Int)

val total = listOf(
    Product("A", 10),
    Product("B", 20)
).sumOf { it.price }

println(total) // 30

11.23 mapNotNull

先映射,再自动去掉 null

val result = listOf("1", "2", "x", "3").mapNotNull { it.toIntOrNull() }
println(result) // [1, 2, 3]

11.24 filterNotNull

过滤掉 null 元素。

val result = listOf(1, null, 2, null, 3).filterNotNull()
println(result) // [1, 2, 3]

11.25 sortedWith

使用自定义比较器排序。

data class User(val name: String, val age: Int)

val users = listOf(
    User("Tom", 20),
    User("Jack", 18),
    User("Lucy", 20)
)

val result = users.sortedWith(compareBy<User> { it.age }.thenBy { it.name })
println(result)

11.26 chunked

把集合按指定大小分块。

val result = listOf(1, 2, 3, 4, 5, 6, 7).chunked(3)
println(result) // [[1, 2, 3], [4, 5, 6], [7]]

11.27 windowed

按滑动窗口取子列表。

val result = listOf(1, 2, 3, 4, 5).windowed(size = 3, step = 1)
println(result) // [[1, 2, 3], [2, 3, 4], [3, 4, 5]]

11.28 runningFold

返回每一步累计过程。

val result = listOf(1, 2, 3).runningFold(0) { acc, item -> acc + item }
println(result) // [0, 1, 3, 6]

11.29 runningReduce

返回每一步 reduce 的累计过程。

val result = listOf(1, 2, 3, 4).runningReduce { acc, item -> acc + item }
println(result) // [1, 3, 6, 10]

11.30 groupingByeachCount

按规则分组并统计每组数量。

val result = listOf("apple", "ape", "banana", "book")
    .groupingBy { it.first() }
    .eachCount()

println(result) // {a=2, b=2}

12. fold 深入理解

fold 是理解高阶函数非常好的例子。

val result = listOf(1, 2, 3).fold(10) { acc, item ->
    acc + item
}

执行过程:

  • 初始值是 10
  • 第一次:10 + 1 = 11
  • 第二次:11 + 2 = 13
  • 第三次:13 + 3 = 16

所以结果是 16

这里:

  • fold 负责控制“遍历和累计”的流程
  • lambda 负责决定“怎么累计”

13. Sequence 与惰性计算

当链式调用很多、数据量很大时,可以使用 Sequence

val result = listOf(1, 2, 3, 4, 5)
    .asSequence()
    .filter { it > 2 }
    .map { it * 2 }
    .take(2)
    .toList()

特点:

  • 惰性执行
  • 中间结果不会马上全部创建
  • 适合长链条操作

14. 作用域函数

作用域函数本质上也是高阶函数,因为它们都接收 lambda。

14.1 let

val length = "kotlin".let {
    println(it)
    it.length
}

特点:

  • 对象以 it 形式传入
  • 返回 lambda 最后一行结果

14.2 run

val result = "kotlin".run {
    uppercase()
}

特点:

  • 对象以 this 形式使用
  • 返回 lambda 最后一行结果

14.3 apply

val list = mutableListOf<Int>().apply {
    add(1)
    add(2)
}

特点:

  • 对象以 this 形式使用
  • 返回对象本身

14.4 also

val text = "hello".also {
    println("原始值: $it")
}

特点:

  • 对象以 it 形式传入
  • 返回对象本身

14.5 with

val result = with(StringBuilder()) {
    append("Hello ")
    append("Kotlin")
    toString()
}

14.6 takeIf

如果对象满足条件,就返回对象本身;否则返回 null

val result = 10.takeIf { it > 5 }
println(result) // 10

val result2 = 3.takeIf { it > 5 }
println(result2) // null

非常适合做“条件保留”:

val text = "Kotlin"
val longText = text.takeIf { it.length > 5 }

14.7 takeUnless

takeIf 相反。

如果条件不成立,就返回对象本身;如果条件成立,返回 null

val result = 3.takeUnless { it > 5 }
println(result) // 3

15. letrunapplyalso 怎么选

一个简单记法:

  • 想返回“结果值”:let / run
  • 想返回“对象本身”:apply / also
  • 想用 itlet / also
  • 想用 thisrun / apply

16. 接收者函数类型

普通函数类型:

(String) -> Int

接收者函数类型:

String.() -> Int

它表示“一个以 String 作为接收者的函数”。

例如:

val countA: String.() -> Int = {
    count { it == 'a' }
}

fun main() {
    println("banana".countA()) // 3
}

这种形式在 DSL 设计中非常常见。


17. 常见字符串函数

虽然这些不全是高阶函数,但它们经常和高阶函数链式配合使用。

17.1 isBlank

判断字符串是否为空或者只包含空白字符。

println("   ".isBlank()) // true
println("abc".isBlank()) // false

17.2 isNotBlank

判断字符串是否不是空白字符串。

println("hello".isNotBlank()) // true
println("   ".isNotBlank())   // false

这个函数经常和 filter 搭配:

val result = listOf("a", " ", "", "kotlin").filter { it.isNotBlank() }
println(result) // [a, kotlin]

17.3 isEmpty

只判断长度是否为 0,不关心是不是空格。

println("".isEmpty())   // true
println(" ".isEmpty())  // false

17.4 isNotEmpty

println("abc".isNotEmpty()) // true

17.5 substringBefore

取指定分隔符前面的内容。

val result = "name=Tom".substringBefore("=")
println(result) // name

17.6 substringAfter

取指定分隔符后面的内容。

val result = "name=Tom".substringAfter("=")
println(result) // Tom

17.7 trim

去掉首尾空白字符。

println("  Kotlin  ".trim()) // Kotlin

17.8 uppercase / lowercase

println("kotlin".uppercase()) // KOTLIN
println("KOTLIN".lowercase()) // kotlin

17.9 startsWith / endsWith

println("kotlin".startsWith("kot")) // true
println("kotlin".endsWith("lin"))   // true

17.10 contains

println("kotlin".contains("lin")) // true

18. 常见数值与范围函数

这些函数也非常高频,尤其在处理边界值时。

18.1 coerceIn

把值限制在某个范围内。

println(5.coerceIn(1, 10))   // 5
println(0.coerceIn(1, 10))   // 1
println(20.coerceIn(1, 10))  // 10

含义:

  • 小于最小值,就返回最小值
  • 大于最大值,就返回最大值
  • 在范围内,就返回原值

18.2 coerceAtLeast

保证值至少是多少。

println(3.coerceAtLeast(5)) // 5
println(8.coerceAtLeast(5)) // 8

18.3 coerceAtMost

保证值至多是多少。

println(12.coerceAtMost(10)) // 10
println(6.coerceAtMost(10))  // 6

18.4 in

判断值是否在范围内。

println(5 in 1..10) // true
println(20 in 1..10) // false

18.5 until

创建左闭右开区间。

for (i in 0 until 5) {
    print(i)
}
// 01234

19. 常见空安全与空值处理函数

19.1 ?.

安全调用。

val length = text?.length

19.2 ?:

Elvis 操作符,空值时给默认值。

val name = input ?: "Unknown"

19.3 requireNotNull

如果为空就抛异常,否则返回非空值。

val text: String? = "hello"
val result = requireNotNull(text)
println(result.length)

19.4 checkNotNull

requireNotNull 类似,但语义偏内部状态检查。

val value: String? = "abc"
val result = checkNotNull(value)
println(result)

19.5 orEmpty

空字符串或空集合常用兜底方法。

val text: String? = null
println(text.orEmpty()) // ""

对于集合:

val list: List<Int>? = null
println(list.orEmpty()) // []

20. 常见标准库工具函数

20.1 run

执行一段代码并返回结果。

val result = run {
    val a = 10
    val b = 20
    a + b
}
println(result) // 30

20.2 repeat

重复执行若干次。

repeat(3) {
    println("index = $it")
}

20.3 lazy

延迟初始化。

val text by lazy {
    println("初始化")
    "Hello"
}

20.4 buildList

构建不可变列表。

val result = buildList {
    add(1)
    add(2)
    add(3)
}

20.5 buildMap

构建不可变 Map。

val result = buildMap {
    put("a", 1)
    put("b", 2)
}

20.6 buildSet

构建不可变 Set。

val result = buildSet {
    add(1)
    add(2)
    add(2)
}

println(result) // [1, 2]

20.7 runCatching

捕获异常并返回 Result

val result = runCatching {
    "123".toInt()
}

println(result.getOrNull()) // 123

如果失败:

val result = runCatching {
    "abc".toInt()
}

println(result.getOrNull()) // null

20.8 getOrNull

Result 里安全取值,失败时返回 null

val result = runCatching { "100".toInt() }
println(result.getOrNull()) // 100

20.9 getOrElse

失败时给默认值。

val result = runCatching { "abc".toInt() }
    .getOrElse { -1 }

println(result) // -1

20.10 getOrDefault

失败时返回指定默认值。

val result = runCatching { "abc".toInt() }
    .getOrDefault(0)

println(result) // 0

20.11 onSuccess

成功时执行附加逻辑。

runCatching { "123".toInt() }
    .onSuccess { println("成功: $it") }

20.12 onFailure

失败时执行附加逻辑。

runCatching { "abc".toInt() }
    .onFailure { println("失败: ${it.message}") }

20.13 use

对实现了 Closeable 的对象自动关闭资源。

import java.io.File

val text = File("demo.txt").bufferedReader().use { reader ->
    reader.readText()
}

use 很重要,因为它能确保资源在使用后自动关闭。


21. 返回规则与 return

Lambda 的最后一行默认作为返回值。

val plusOne: (Int) -> Int = { it + 1 }

return 在 lambda 中有时比较特殊。

17.1 标签返回

fun main() {
    listOf(1, 2, 3).forEach {
        if (it == 2) return@forEach
        println(it)
    }
}

这里 return@forEach 表示只退出本次 lambda,不退出外层函数。

17.2 非局部返回

在某些 inline 场景中,lambda 里的 return 可能直接返回外层函数。

fun test() {
    listOf(1, 2, 3).forEach {
        if (it == 2) return
    }
    println("不会执行")
}

22. inline 内联函数

高阶函数很方便,但有时会带来额外开销。inline 可以让编译器把函数体展开到调用处。

inline fun measure(block: () -> Unit) {
    val start = System.currentTimeMillis()
    block()
    println(System.currentTimeMillis() - start)
}

18.1 为什么需要 inline

  • 减少函数对象创建
  • 减少调用开销
  • 支持某些非局部返回能力

18.2 不要滥用

如果函数很大,过度内联会让字节码膨胀。


23. noinlinecrossinline

19.1 noinline

表示某个 lambda 参数不要被内联。

inline fun doSomething(
    inlined: () -> Unit,
    noinline normal: () -> Unit
) {
    inlined()
    normal()
}

19.2 crossinline

表示允许内联,但不允许非局部返回。

inline fun runTask(crossinline action: () -> Unit) {
    val runnable = Runnable {
        action()
    }
    runnable.run()
}

24. SAM 转换

如果一个 Java 接口只有一个抽象方法,Kotlin 可以直接用 lambda 替代它。

例如 Runnable

val runnable = Runnable {
    println("running")
}

这叫 SAM 转换。

它在和 Java API 交互时非常常见。


25. 协程中的高阶函数

协程 API 本身就大量使用高阶函数。

21.1 launch

scope.launch {
    println("执行协程任务")
}

launch 接收一个 lambda,这个 lambda 会在协程中执行。

21.2 async

val deferred = scope.async {
    10 + 20
}

println(deferred.await()) // 30

21.3 withContext

val result = withContext(Dispatchers.Default) {
    (1..1000).sum()
}

21.4 coroutineScope

suspend fun loadAll() = coroutineScope {
    val a = async { 10 }
    val b = async { 20 }
    a.await() + b.await()
}

25.5 suspendCoroutine

把回调风格 API 包装成挂起函数时很常用。

import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine

suspend fun loadValue(): Int = suspendCoroutine { continuation ->
    val resultFromCallback = 42
    continuation.resume(resultFromCallback)
}

这里的 resume 不是高阶函数,但它是协程学习里非常重要的函数。

含义:

  • 当前挂起函数先暂停
  • 等回调结果回来后,调用 resume(value)
  • 协程从暂停点继续执行,并把 value 作为结果返回

如果失败,可以使用:

import kotlin.coroutines.resumeWithException

例如:

import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine

suspend fun requestData(success: Boolean): String = suspendCoroutine { continuation ->
    if (success) {
        continuation.resume("OK")
    } else {
        continuation.resumeWithException(IllegalStateException("Request failed"))
    }
}

26. Flow 中的高阶函数

Flow 是 Kotlin 协程里的数据流类型,它的操作几乎全都是高阶函数。

22.1 collect

flow.collect { value ->
    println(value)
}

22.2 collectLatest

flow.collectLatest { value ->
    println("最新值: $value")
}

22.3 map

flow.map { it * 2 }

22.4 filter

flow.filter { it > 0 }

22.5 onEach

flow.onEach { println("观察到: $it") }

22.6 catch

flow.catch { e ->
    println("异常: ${e.message}")
}

22.7 debounce

flow.debounce(300)

22.8 flatMapLatest

flow.flatMapLatest { value ->
    otherFlow(value)
}

27. 其他非常常见但容易漏掉的函数

这一节把学习 Kotlin 时很常遇到的高频函数再补充一批。

27.1 first

获取第一个元素,不存在会抛异常。

val result = listOf(10, 20, 30).first()
println(result) // 10

27.2 last

获取最后一个元素。

val result = listOf(10, 20, 30).last()
println(result) // 30

27.3 single

要求集合中只有一个元素,否则抛异常。

val result = listOf(42).single()
println(result) // 42

27.4 singleOrNull

如果刚好一个元素就返回,否则返回 null

println(listOf(42).singleOrNull())   // 42
println(listOf(1, 2).singleOrNull()) // null

27.5 joinToString

把集合拼成字符串。

val result = listOf("a", "b", "c").joinToString(", ")
println(result) // a, b, c

27.6 getOrNull

按下标取元素,越界返回 null

val result = listOf(10, 20).getOrNull(5)
println(result) // null

27.7 ifEmpty

如果集合或字符串为空,则给出替代值。

val result = emptyList<Int>().ifEmpty { listOf(1, 2, 3) }
println(result) // [1, 2, 3]

27.8 ifBlank

如果字符串为空白,则给出替代值。

val result = "   ".ifBlank { "default" }
println(result) // default

27.9 ifEmpty 用在字符串上

val result = "".ifEmpty { "fallback" }
println(result) // fallback

27.10 maxByOrNull / minByOrNull

按某个规则找最大或最小元素。

data class Student(val name: String, val score: Int)

val students = listOf(
    Student("Tom", 80),
    Student("Jack", 95),
    Student("Lucy", 70)
)

val top = students.maxByOrNull { it.score }
println(top) // Student(name=Jack, score=95)

27.11 maxOfOrNull / minOfOrNull

直接取某个字段的最大值或最小值。

val result = listOf(10, 50, 30).maxOfOrNull { it }
println(result) // 50

27.12 compareBy

创建比较规则。

data class Person(val name: String, val age: Int)

val people = listOf(
    Person("Tom", 20),
    Person("Jack", 18),
    Person("Lucy", 20)
)

val result = people.sortedWith(compareBy<Person> { it.age }.thenBy { it.name })
println(result)

27.13 onEach

在链式调用中插入副作用,不改变元素本身。

val result = listOf(1, 2, 3)
    .onEach { println("元素: $it") }
    .map { it * 2 }

println(result) // [2, 4, 6]

27.14 also

常用于调试、日志、附加动作。

val result = mutableListOf(1, 2, 3).also {
    println("原始列表: $it")
}

27.15 apply

常用于对象初始化。

val sb = StringBuilder().apply {
    append("Hello")
    append(" Kotlin")
}

println(sb.toString())

27.16 repeat

重复执行一段代码。

repeat(3) { index ->
    println("index = $index")
}

27.17 binarySearch

对有序集合做二分查找。

val list = listOf(1, 3, 5, 7, 9)
println(list.binarySearch(5)) // 2

27.18 random

随机取一个元素。

val result = listOf("a", "b", "c").random()
println(result)

27.19 shuffled

打乱集合顺序。

val result = listOf(1, 2, 3, 4, 5).shuffled()
println(result)

27.20 reversed

返回倒序集合。

val result = listOf(1, 2, 3).reversed()
println(result) // [3, 2, 1]

27.21 reversedArray

返回倒序数组。

val arr = arrayOf(1, 2, 3)
println(arr.reversedArray().joinToString())

27.22 toIntOrNull / toLongOrNull

安全转换数字,失败时返回 null

println("123".toIntOrNull()) // 123
println("abc".toIntOrNull()) // null

27.23 toBooleanStrictOrNull

严格解析布尔值,非法字符串返回 null

println("true".toBooleanStrictOrNull())  // true
println("yes".toBooleanStrictOrNull())   // null

27.24 contentEquals

比较数组内容是否相等。

val a = arrayOf(1, 2, 3)
val b = arrayOf(1, 2, 3)
println(a.contentEquals(b)) // true

27.25 contentDeepEquals

比较嵌套数组内容是否深度相等。

val a = arrayOf(arrayOf(1, 2), arrayOf(3, 4))
val b = arrayOf(arrayOf(1, 2), arrayOf(3, 4))
println(a.contentDeepEquals(b)) // true

28. 常用函数分类速查表

下面这张速查表更适合平时回忆。

集合处理

  • map:转换
  • mapNotNull:转换并去空
  • filter:过滤
  • filterNotNull:过滤空值
  • flatMap:展开
  • distinct:去重
  • groupBy:分组
  • groupingBy:高级分组入口
  • eachCount:统计每组数量
  • associate:转 Map
  • zip:配对
  • chunked:分块
  • windowed:滑动窗口

聚合统计

  • sumOf:求和
  • count:计数
  • any:是否存在
  • all:是否全部满足
  • none:是否没有
  • fold:带初始值累计
  • reduce:不带初始值累计
  • runningFold:保留累计过程
  • runningReduce:保留 reduce 过程

排序与比较

  • sortedBy:升序排序
  • sortedByDescending:降序排序
  • sortedWith:自定义比较
  • compareBy:创建比较规则
  • maxByOrNull:按规则取最大元素
  • minByOrNull:按规则取最小元素
  • maxOfOrNull:取最大值
  • minOfOrNull:取最小值

空安全

  • ?.:安全调用
  • ?::空值默认
  • let:非空后继续处理
  • takeIf:满足条件才保留
  • takeUnless:不满足条件才保留
  • orEmpty:空值转默认空对象
  • ifEmpty:空时替换
  • ifBlank:空白时替换
  • requireNotNull:判空并抛异常
  • checkNotNull:内部状态判空

字符串

  • isBlank:是否空白
  • isNotBlank:是否非空白
  • trim:去首尾空格
  • substringBefore:取前半部分
  • substringAfter:取后半部分
  • uppercase:转大写
  • lowercase:转小写
  • startsWith:前缀判断
  • endsWith:后缀判断

数值与范围

  • coerceIn:限制范围
  • coerceAtLeast:至少多少
  • coerceAtMost:最多多少
  • in:判断是否在范围中
  • until:左闭右开区间

异常处理

  • runCatching:捕获异常
  • getOrNull:失败返回 null
  • getOrElse:失败时用函数兜底
  • getOrDefault:失败时用默认值
  • onSuccess:成功回调
  • onFailure:失败回调

协程

  • launch:启动协程
  • async:异步并返回结果
  • withContext:切换上下文
  • coroutineScope:结构化并发
  • suspendCoroutine:回调转挂起
  • resume:恢复协程
  • resumeWithException:恢复并抛异常

29. 高阶函数与 DSL

Kotlin 能写出很多优雅 DSL,很大程度上依赖高阶函数和接收者 lambda。

例如:

class UserBuilder {
    var name: String = ""
    var age: Int = 0

    fun build(): User = User(name, age)
}

data class User(val name: String, val age: Int)

fun buildUser(block: UserBuilder.() -> Unit): User {
    val builder = UserBuilder()
    builder.block()
    return builder.build()
}

fun main() {
    val user = buildUser {
        name = "Tom"
        age = 18
    }
    println(user)
}

30. 什么时候适合用高阶函数

适合:

  • 有重复的控制流程
  • 行为规则可以抽出来单独传入
  • 想提高代码复用性
  • 想写链式、声明式风格

例如:

  • 遍历和筛选
  • 事件回调
  • 异步任务执行
  • 构建 DSL
  • 状态转换

31. 什么时候不要滥用

虽然高阶函数很强,但也不是所有地方都适合。

不建议滥用在:

  • 逻辑过于复杂导致 lambda 套 lambda
  • 参数太多导致阅读困难
  • 闭包修改大量外部状态
  • 为了“炫技巧”而让代码难懂

一个判断标准:

如果别人读起来比普通函数更费劲,那就可能写过头了。


32. 常见误区

26.1 以为所有带大括号的都是高阶函数

不是。

只有“函数接收另一个函数作为参数”时,才是高阶函数场景。

26.2 搞不清 it

it 只是单参数 lambda 的默认名字。

26.3 以为 map 会修改原集合

不会。

map 返回的是新集合。

26.4 以为 forEachmap 一样

不一样。

  • forEach:执行操作,通常返回 Unit
  • map:转换数据,返回新集合

26.5 以为作用域函数只是语法糖

它们不只是简写,也是在统一“对象作用域中的操作方式”。

32.6 以为常用函数越多越好

不是。

函数多是为了表达更精准,不是为了炫技巧。 如果 map/filter/let/apply 写得比普通逻辑还绕,就应该收一收。


33. 记忆口诀

集合函数

  • map:变
  • filter:筛
  • forEach:做
  • fold:累
  • flatMap:拆开再铺平

作用域函数

  • let:拿 it 算结果
  • run:拿 this 算结果
  • apply:拿 this 改对象,最后还对象
  • also:拿 it 做附加操作,最后还对象

高频工具函数

  • takeIf:满足条件才留下
  • takeUnless:不满足条件才留下
  • distinct:去重
  • any:有没有至少一个
  • all:是不是全部都满足
  • none:是不是一个都没有
  • coerceIn:强行压到范围里
  • isNotBlank:不是空白字符串
  • resume:让挂起的协程继续往下走

34. 学习顺序建议

推荐按这个顺序学:

  1. 普通函数和函数类型
  2. Lambda 基础
  3. it 和尾随 lambda
  4. 集合高阶函数
  5. 作用域函数
  6. 匿名函数与函数引用
  7. 内联函数
  8. 协程中的高阶函数
  9. Flow 操作符
  10. DSL 与接收者 lambda

35. 综合练习

练习 1:接收函数参数

fun operate(a: Int, b: Int, op: (Int, Int) -> Int): Int {
    return op(a, b)
}

尝试传入:

  • 加法
  • 减法
  • 乘法

练习 2:返回函数

fun createMultiplier(factor: Int): (Int) -> Int {
    return { value -> value * factor }
}

练习 3:集合处理

val nums = listOf(1, 2, 3, 4, 5)
val result = nums.filter { it > 2 }.map { it * it }

思考结果是什么。

练习 4:作用域函数

val text = StringBuilder().apply {
    append("Hello")
    append(" Kotlin")
}.toString()

练习 5:fold

val result = listOf(1, 2, 3, 4).fold(1) { acc, item ->
    acc * item
}

思考结果是什么。

练习 6:takeIf

val age = 20
val adultAge = age.takeIf { it >= 18 }
println(adultAge)

思考:

  • 如果 age = 15,结果是什么

练习 7:distinct

val result = listOf("a", "b", "a", "c", "b").distinct()
println(result)

练习 8:coerceIn

println((-5).coerceIn(0, 100))
println(150.coerceIn(0, 100))
println(60.coerceIn(0, 100))

练习 9:isNotBlank

val names = listOf("Tom", " ", "", "Jack")
val result = names.filter { it.isNotBlank() }
println(result)

练习 10:mapNotNull

val result = listOf("1", "x", "2", "3a", "4").mapNotNull { it.toIntOrNull() }
println(result)

练习 11:runCatching

val result = runCatching { "abc".toInt() }
println(result.getOrDefault(-1))

练习 12:groupingBy

val result = listOf("cat", "car", "dog", "door")
    .groupingBy { it.first() }
    .eachCount()

println(result)

练习 13:chunked

val result = (1..10).toList().chunked(4)
println(result)

练习 14:takeUnless

val text = "Kotlin"
val result = text.takeUnless { it.length < 3 }
println(result)

36. 一页总结

高阶函数的本质是:

“把行为当成值来传递。”

Kotlin 中几乎所有声明式风格都围绕它展开:

  • 集合操作
  • 作用域函数
  • 协程
  • Flow
  • DSL

你只要每次都问自己两个问题,就会更容易看懂:

  1. 这里哪个函数在接收 lambda
  2. 这个 lambda 到底什么时候执行

把这两个问题想清楚,Kotlin 高阶函数就不神秘了。


37. 参考资料

以下资料可继续深入学习: