Kotlin 提供了丰富的内置函数,它们极大简化了日常开发工作。常见内置函数包括 标准库函数(let、apply、run 等),用于提高代码的简洁性和可读性。下面我们详细介绍这些函数的功能、用法以及它们之间的区别。
let 通常用于避免空指针异常 (null) 或执行某个块逻辑并返回结果。
示例 1:let 实现空安全
val name: String? = "Kotlin"
name?.let {
println("The name is $it")
} // 如果 name 是 null,不会执行 let 块
示例 2:链式调用
val result = "Kotlin".let {
println("Processing: $it")
it.length
}.let {
println("Length: $it")
it * 2
}
println("Final Result: $result")
输出:
Processing: Kotlin
Length: 6
Final Result: 12
实例3:和java相比,let配合?能让我们避免写if判空的同时确定了变量的作用范围
// 使用Java
if( mVar != null ){
mVar.function1();
mVar.function2();
mVar.function3();
}
// 使用kotlin(无使用let函数)
mVar?.function1()
mVar?.function2()
mVar?.function3()
// 使用kotlin(使用let函数)
// 方便了统一判空的处理 & 确定了mVar变量的作用域
mVar?.let {
it.function1()
it.function2()
it.function3()
}
apply 返回对象本身,通常用于对象的初始化或批量赋值。
示例 1:初始化对象
data class Person(var name: String, var age: Int)
val person = Person("Unknown", 0).apply {
name = "Alice"
age = 25
}
println(person) // 输出:Person(name=Alice, age=25)
如果初始化对象是map类型:
fun main() {
var people: MutableMap = mutableMapOf()
people.apply {
this["name"] = "Alice"
this["age"] = 25
}
println(people) // 输出:{name=Alice, age=25}
}
示例 2:配合嵌套初始化
val person = Person("Unknown", 0).apply {
name = "Bob"
age = 30
}.apply {
println("Configured person: $this")
}
run 用于对对象执行代码块并返回结果,常用于需要执行多个操作且返回最后结果的场景,run可以看成是let+with,可以在判空的同时省去写对象名
示例 1:对象操作后返回结果
val introduction = Person("John", 20).run {
"Hi, my name is $name and I am $age years old."
}
println(introduction) // 输出:Hi, my name is John and I am 20 years old.
示例2:结合Elvis 运算符 ?:
class UserNotFoundException(message: String) : Exception(message)
data class User(val id: String, val name: String)
fun getUserById(userId: String): User? {
// 假设这是从数据库或其他数据源获取用户的逻辑
return null // 模拟未找到用户
}
fun main() {
val userId = "12345"
val user = getUserById(userId) ?: run {
// 如果 user 是 null,则执行此块
val msg = "UserNotFound: User with ID $userId could not be found."
println("Error: $msg") // 模拟 logger.error(msg)
throw UserNotFoundException(msg)
}
// 如果找到了用户,则继续执行
println("User found: ${user.name}")
}
效果等价于:
class UserNotFoundException(message: String) : Exception(message)
data class User(val id: String, val name: String)
fun getUserById(userId: String): User? {
// 假设这是从数据库或其他数据源获取用户的逻辑
return null // 模拟未找到用户
}
fun main() {
val userId = "12345"
val user = getUserById(userId)
if (user==null){
// 如果 user 是 null,则执行此块
val msg = "UserNotFound: User with ID $userId could not be found."
println("Error: $msg") // 模拟 logger.error(msg)
throw UserNotFoundException(msg)
}
// 如果找到了用户,则继续执行
println("User found: ${user.name}")
}
also 主要用于链式调用中的副作用操作,比如打印日志或检查条件。
示例 1:日志输出辅助
val numbers = mutableListOf(1, 2, 3).also {
println("Initial list: $it")
}.apply {
add(4)
}.also {
println("Updated list: $it")
}
输出:
Initial list: [1, 2, 3]
Updated list: [1, 2, 3, 4]
示例2:和let类似,区别在于let函数返回值是最后一行,also是返回传入的对象本身
// let函数
var result = mVar.let {
it.function1()
it.function2()
it.function3()
999
}
// 最终结果 = 返回999给变量result
// also函数
var result = mVar.also {
it.function1()
it.function2()
it.function3()
999
}
// 最终结果 = 返回一个mVar对象给变量result
with 是非扩展函数,适合处理需要多次使用同一对象的场景,用with函数可以在调用同一个对象的多个方法或属性时,可以省去对象名重复,直接调用方法名或属性
示例 1:使用 with 简化代码
val person = Person("Alice", 25)
val introduction = with(person) {
"My name is $name and I am $age years old."
}
println(introduction) // 输出:My name is Alice and I am 25 years old.
返回值是函数的最后一行
map
是一个用于转换集合中每个元素的函数。它会遍历集合,将每个元素应用一个转换函数,返回一个新的集合,新的集合包含转换后的元素。
val newList = oldList.map { item -> transform(item) }
val numbers = listOf(1, 2, 3, 4)
val squaredNumbers = numbers.map { it * it }
println(squaredNumbers) // 输出: [1, 4, 9, 16]
在上面的例子中,map
将 numbers
中的每个元素平方,并返回一个新的集合 [1, 4, 9, 16]
。
reduce
是一个聚合函数,用于将集合中的所有元素根据某种操作进行聚合。它将集合中的元素逐一累加(或进行其他自定义操作),直到最终得到一个结果。
val result = collection.reduce { acc, item -> operation(acc, item) }
val numbers = listOf(1, 2, 3, 4)
val sum = numbers.reduce { acc, item -> acc + item }
println(sum) // 输出: 10
在上面的例子中,reduce
将 numbers
中的所有元素加起来,返回结果 10
。acc
是累积的中间结果,item
是当前元素。
reduce
函数必须至少有一个元素,否则会抛出 NoSuchElementException
。reduceOrNull
或 fold
。zip
函数将两个集合按元素一一对应地合并成一个新的集合。每个元素组合成一个对偶(pair)。如果两个集合的大小不同,zip
会根据较小的集合的长度来进行合并。
val result = collection1.zip(collection2)
val names = listOf("Alice", "Bob", "Charlie")
val ages = listOf(25, 30, 35)
val nameAgePairs = names.zip(ages)
println(nameAgePairs) // 输出: [(Alice, 25), (Bob, 30), (Charlie, 35)]
在上面的例子中,zip
将 names
和 ages
这两个集合对应的元素合并成一个新的集合,形成 (name, age)
的对偶。
zip
会使用最小集合的长度。zipWithNext
来将集合中的元素与下一个元素进行组合。场景:初始化对象、打印日志并返回处理结果
data class Car(var brand: String, var year: Int)
val car = Car("Unknown", 0).apply {
brand = "Tesla"
year = 2022
}.also {
println("Car initialized: $it")
}.run {
"The car is a $brand from $year."
}.let { result ->
println("Final Result: $result")
}
输出:
Car initialized: Car(brand=Tesla, year=2022)
Final Result: The car is a Tesla from 2022.
解释:
使用 apply 初始化对象。
用 also 打印日志。
用 run 返回拼接后的字符串结果。
最后用 let 处理并打印结果。
场景:用户输入处理、日志记录、结果返回
fun processInput(input: String?) {
input?.let {
it.trim()
}?.also {
println("Trimmed input: $it")
}?.run {
if (isNotEmpty()) this.uppercase() else "EMPTY"
}?.let { result ->
println("Processed Result: $result")
}
}
processInput(" Kotlin ") // 输入非空字符串
processInput(" ") // 输入空格
输出:
Trimmed input: Kotlin
Processed Result: KOTLIN
Trimmed input:
Processed Result: EMPTY
解释:
let 用于处理非空输入并去除空格。
also 打印中间结果。
run 检查是否为空,返回大写结果或 “EMPTY”。
let 打印最终处理结果。
综合示例:map, reduce 和 zip
我们可以结合 map
、reduce
和 zip
来完成更复杂的操作。例如,假设我们有两个人的姓名和年龄列表,我们想要计算年龄的总和,并且将它们与原始姓名配对。
val names = listOf("Alice", "Bob", "Charlie")
val ages = listOf(25, 30, 35)
// 使用 zip 合并 names 和 ages
val nameAgePairs = names.zip(ages)
println(nameAgePairs) // 输出: [(Alice, 25), (Bob, 30), (Charlie, 35)]
// 使用 map 创建年龄的平方列表
val ageSquares = ages.map { it * it }
println(ageSquares) // 输出: [625, 900, 1225]
// 使用 reduce 求和
val totalAge = ages.reduce { acc, age -> acc + age }
println(totalAge) // 输出: 90
结合 map
和 reduce
实现列表求和:
val numbers = listOf(1, 2, 3, 4)
val sum = numbers.map { it * 2 }.reduce { acc, item -> acc + item }
println(sum) // 输出: 20
使用 zip
和 map
对多个列表进行处理:
val names = listOf("Alice", "Bob", "Charlie")
val scores = listOf(90, 80, 85)
val nameScorePairs = names.zip(scores).map { "${it.first}: ${it.second}" }
println(nameScorePairs) // 输出: [Alice: 90, Bob: 80, Charlie: 85]
用 zip
和 reduce
计算两个列表中对应位置元素的差值和:
val list1 = listOf(10, 20, 30)
val list2 = listOf(1, 2, 3)
val totalDifference = list1.zip(list2).map { it.first - it.second }.reduce { acc, diff -> acc + diff }
println(totalDifference) // 输出: 24
函数 | 返回值 | 接收者 | 主要用途 | 嵌套场景 |
---|---|---|---|---|
let | Lambda 结果 | it | 对非空对象操作并返回结果 | 处理结果或链式调用 |
apply | 对象本身 | this | 初始化对象或配置属性 | 初始化链式调用 |
run | Lambda 结果 | this | 对对象执行操作并返回结果 | 逻辑处理后返回 |
also | 对象本身 | it | 执行副作用操作(如日志) | 调试、日志记录 |
with | Lambda 结果 | this | 多次调用同一对象 | 简化代码结构 |
map | 转换后的集合 | 集合(List/Set等) | 对每个元素应用转换函数,返回新集合 | 可以与其他操作(如 filter )一起使用 |
reduce | 单一的聚合结果(通常是单一值) | 集合(List/Set等) | 聚合集合中的元素(如求和、求最大值等) | 可以嵌套其他函数(如 map )来进行计算 |
zip | 由对偶(Pair)组成的集合 | 两个集合 | 将两个集合的元素按位置一一组合成对偶,形成新的集合 | 可以与 map 或 reduce 结合使用 |
let
:适合处理非空对象,并返回结果。常用于链式调用。apply
:适合初始化对象,返回对象本身。适用于配置初始化对象。run
:适合执行操作并返回结果,用于逻辑处理。also
:适合执行副作用操作,不改变返回值。常用于日志记录或调试。with
:适合需要多次使用同一对象的场景。map
用于转换集合中的每个元素,返回一个新的集合。reduce
用于将集合中的元素聚合成一个单一结果,常用于求和、求最大值等场景。zip
用于将两个集合按位置一一对应,返回一个由对偶(Pair)组成的集合。