Kotlin的内置函数

Kotlin 提供了丰富的内置函数,它们极大简化了日常开发工作。常见内置函数包括 标准库函数(let、apply、run 等),用于提高代码的简洁性和可读性。下面我们详细介绍这些函数的功能、用法以及它们之间的区别。

1. let 函数

let 通常用于避免空指针异常 (null) 或执行某个块逻辑并返回结果。
Kotlin的内置函数_第1张图片

示例 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()
}

2. apply 函数

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")
}

3. run 函数

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}")
}

4. also 函数

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

5. with 函数

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.

返回值是函数的最后一行

6. map

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]

在上面的例子中,mapnumbers 中的每个元素平方,并返回一个新的集合 [1, 4, 9, 16]

7. reduce

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

在上面的例子中,reducenumbers 中的所有元素加起来,返回结果 10acc 是累积的中间结果,item 是当前元素。

注意:
  • reduce 函数必须至少有一个元素,否则会抛出 NoSuchElementException
  • 如果你希望有默认值,可以使用 reduceOrNullfold

8. zip

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)]

在上面的例子中,zipnamesages 这两个集合对应的元素合并成一个新的集合,形成 (name, age) 的对偶。

注意:
  • 如果两个集合的大小不同,zip 会使用最小集合的长度。
  • 可以使用 zipWithNext 来将集合中的元素与下一个元素进行组合。

9. 内置函数嵌套使用

场景:初始化对象、打印日志并返回处理结果

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
我们可以结合 mapreducezip 来完成更复杂的操作。例如,假设我们有两个人的姓名和年龄列表,我们想要计算年龄的总和,并且将它们与原始姓名配对。

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
  • 结合 mapreduce 实现列表求和

    val numbers = listOf(1, 2, 3, 4)
    val sum = numbers.map { it * 2 }.reduce { acc, item -> acc + item }
    println(sum)  // 输出: 20
    
  • 使用 zipmap 对多个列表进行处理

    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]
    
  • zipreduce 计算两个列表中对应位置元素的差值和

    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
    

10. 内置函数对比总结

函数 返回值 接收者 主要用途 嵌套场景
let Lambda 结果 it 对非空对象操作并返回结果 处理结果或链式调用
apply 对象本身 this 初始化对象或配置属性 初始化链式调用
run Lambda 结果 this 对对象执行操作并返回结果 逻辑处理后返回
also 对象本身 it 执行副作用操作(如日志) 调试、日志记录
with Lambda 结果 this 多次调用同一对象 简化代码结构
map 转换后的集合 集合(List/Set等) 对每个元素应用转换函数,返回新集合 可以与其他操作(如 filter)一起使用
reduce 单一的聚合结果(通常是单一值) 集合(List/Set等) 聚合集合中的元素(如求和、求最大值等) 可以嵌套其他函数(如 map)来进行计算
zip 由对偶(Pair)组成的集合 两个集合 将两个集合的元素按位置一一组合成对偶,形成新的集合 可以与 mapreduce 结合使用
  • let:适合处理非空对象,并返回结果。常用于链式调用。
  • apply:适合初始化对象,返回对象本身。适用于配置初始化对象。
  • run:适合执行操作并返回结果,用于逻辑处理。
  • also:适合执行副作用操作,不改变返回值。常用于日志记录或调试。
  • with:适合需要多次使用同一对象的场景。
  • map 用于转换集合中的每个元素,返回一个新的集合。
  • reduce 用于将集合中的元素聚合成一个单一结果,常用于求和、求最大值等场景。
  • zip 用于将两个集合按位置一一对应,返回一个由对偶(Pair)组成的集合。

你可能感兴趣的:(kotlin,kotlin,windows,microsoft)