25、Kotlin标准库:标准库概览、常用扩展函数、作用域函数、集合操作

说实话,Kotlin 的标准库是我最喜欢它的原因之一。很多语言的标准库就是给你一堆基础工具,够用但谈不上好用。Kotlin 不一样,它把很多我们日常开发中高频使用的模式,直接封装成了标准库函数。你想想看,写 Java 的时候,处理一个集合要写多少样板代码?在 Kotlin 里,一行搞定。

这一章,我会带你快速过一遍标准库的核心内容。不是那种枯燥的 API 罗列,而是挑出我工作中真正高频使用的东西:扩展函数、作用域函数、集合操作。这些东西你学会了,写代码的效率能翻一倍。

Kotlin 标准库核心知识体系 Kotlin 标准库 常用扩展函数 apply / let / run / also / with toIntOrNull / isNullOrBlank 作用域函数 let / run / with / apply / also 上下文对象 vs 返回值 集合操作 filter / map / flatMap / groupBy reduce / fold / sortedBy 核心思想:用函数式风格替代命令式样板代码 减少临时变量 · 提升可读性 · 避免空指针

标准库概览:你每天都在用,只是没意识到

Kotlin 标准库(kotlin-stdlib)是每个 Kotlin 项目默认引入的依赖。它不像 Java 那样把很多东西扔给第三方,而是内置了大量实用工具。我个人把它分成三大块:

  • 核心语言扩展:比如 StringCollectionMap 上的扩展函数,还有 PairTriple 这些数据容器。
  • 作用域函数letrunwithapplyalso,这五个函数是 Kotlin 的招牌特性。
  • 集合操作filtermapreduce 等函数式 API,让集合处理变得优雅。

嗯,这里要注意:标准库还包含 kotlinx.coroutines 这样的协程库,但那是进阶内容,我们放到后面章节讲。这一章先聚焦最基础、最常用的部分。

常用扩展函数:给已有类加点「私货」

扩展函数是 Kotlin 最让我上头的特性之一。你可以给一个类添加新方法,而不需要继承它。说白了,就是「偷懒」的合法途径。

举个例子,我经常需要把字符串转成整数,但用户输入可能不合法。Java 里你要写 try-catch,Kotlin 里直接这样:

val input: String = "123"
val number: Int? = input.toIntOrNull()  // 如果解析失败,返回 null,不会抛异常
println(number)  // 输出: 123

val badInput = "abc"
val badNumber = badInput.toIntOrNull()
println(badNumber)  // 输出: null

我在项目中遇到过好几次因为 NumberFormatException 导致崩溃的情况。自从用了 toIntOrNull,这类 bug 几乎绝迹了。类似的还有 isNullOrBlank()isNullOrEmpty(),都是日常高频使用的扩展。

小技巧: 你可以在任意 Kotlin 文件顶层定义自己的扩展函数。比如给 String 加一个 isValidEmail() 方法,整个项目都能用。但别滥用,扩展函数适合「通用工具」,不适合业务逻辑。

作用域函数:let、run、with、apply、also

这五个函数是 Kotlin 的「五虎上将」。很多初学者容易搞混它们,其实记住两个维度就够了:上下文对象怎么访问it 还是 this),以及返回值是什么(上下文对象本身还是 lambda 结果)。

我画了个表格,你一看就明白:

函数 上下文对象 返回值 典型场景
let it lambda 结果 非空判断 + 链式调用
run this lambda 结果 对象配置 + 计算结果
with this lambda 结果 对同一个对象做多次操作
apply this 上下文对象本身 对象初始化配置
also it 上下文对象本身 附加操作(如日志、校验)

来,看几个代码例子,你就知道它们怎么用了。

let:我最常用的非空处理

val name: String? = "Kotlin"
name?.let {
    // 这里 it 就是 name,且保证非空
    println("名字长度: ${it.length}")
}
// 如果 name 是 null,let 里的代码不会执行

我曾经在项目里看到有人写 if (name != null) { ... },然后里面还要再解包。用 let 配合安全调用 ?.,代码简洁多了。

apply:配置对象的神器

val user = User().apply {
    name = "张三"
    age = 28
    email = "zhangsan@example.com"
}
// apply 返回的是 User 对象本身,所以可以直接赋值给 user

你想想看,Java 里你要 new 一个对象,然后一行一行 set。Kotlin 里用 apply,所有配置写在一个块里,代码结构清晰,还省了临时变量。

also:顺手打个日志

val list = mutableListOf(1, 2, 3).also {
    println("初始列表: $it")
}
list.add(4)
// also 返回列表本身,不影响后续操作

also 适合做「副作用」操作,比如日志、校验、缓存。它不会改变返回值,只是「顺便」做点事。

避坑指南: 我曾经在团队 code review 时看到有人嵌套三层作用域函数,代码可读性极差。记住:作用域函数是为了让代码更清晰,不是为了炫技。如果嵌套超过两层,建议拆成独立函数。

集合操作:告别 for 循环

Kotlin 的集合操作是我从 Java 转过来后最爽的体验之一。以前写 Java 要写 for 循环、if 判断、临时变量,现在一行链式调用搞定。

filter + map:最经典的组合

val numbers = listOf(1, 2, 3, 4, 5, 6)
val result = numbers
    .filter { it % 2 == 0 }   // 筛选偶数
    .map { it * it }          // 每个数平方
println(result)  // 输出: [4, 16, 36]

你看,没有临时变量,没有 for 循环,代码读起来就像在描述「我要做什么」,而不是「我要怎么做」。这就是函数式编程的魅力。

groupBy:分组统计

data class Person(val name: String, val age: Int)
val people = listOf(
    Person("张三", 25),
    Person("李四", 30),
    Person("王五", 25)
)
val grouped = people.groupBy { it.age }
println(grouped)
// 输出: {25=[Person(张三,25), Person(王五,25)], 30=[Person(李四,30)]}

我在项目中处理报表数据时,groupBy 几乎是必用的。按日期、按分类、按状态分组,一行代码搞定。

reduce 和 fold:聚合计算

val numbers = listOf(1, 2, 3, 4)
val sum = numbers.reduce { acc, i -> acc + i }
println(sum)  // 输出: 10

// fold 可以指定初始值
val sumWithInit = numbers.fold(10) { acc, i -> acc + i }
println(sumWithInit)  // 输出: 20

reducefold 的区别在于:reduce 用第一个元素作为初始值,fold 让你自己指定。我个人习惯用 fold,因为更明确,不容易出错。

sortedBy:排序

val names = listOf("Charlie", "Alice", "Bob")
val sorted = names.sortedBy { it.length }
println(sorted)  // 输出: [Bob, Alice, Charlie]

排序也是高频操作。注意 sortedBy 返回新列表,原列表不变。如果你要原地排序,用 sortBy(只对可变列表有效)。

核心要点: Kotlin 集合操作遵循「不变性」原则——大多数操作返回新集合,不修改原数据。这能避免很多并发问题。如果你需要修改原集合,使用 MutableList 上的 sort()add() 等方法。

避坑指南:集合操作的性能陷阱

嗯,这里我要说一个我踩过的坑。链式调用虽然爽,但每个操作都会创建中间集合。比如:

val result = list
    .filter { ... }   // 创建第一个新列表
    .map { ... }      // 创建第二个新列表
    .sortedBy { ... } // 创建第三个新列表

如果数据量很大(比如上万条),这种写法会创建多个临时集合,内存开销不小。我曾经在数据处理管道里这么写,结果 OOM 了。解决方案是用 Sequence(序列):

val result = list.asSequence()
    .filter { ... }
    .map { ... }
    .sortedBy { ... }
    .toList()  // 只在最后一步创建集合

Sequence 是惰性求值的,每个元素经过整个管道后才处理下一个,不会创建中间集合。数据量大时,性能差距很明显。

总结

Kotlin 标准库的核心价值,就是让你用更少的代码,表达更清晰的意图。扩展函数让你给已有类添加能力,作用域函数帮你管理上下文和返回值,集合操作让你告别丑陋的 for 循环。

我个人建议:刚开始用的时候,先记住 letapply 这两个最常用的,其他用到再查。别试图一次性记住所有函数,写多了自然就熟了。

最后送你一句话:好的代码不是写出来的,是「表达」出来的。Kotlin 标准库给了你表达的工具,剩下的就看你怎么用了。


公众号:蓝海资料掘金营,微信deep3321