Kotlin学习笔记(10)--作用域函数

作用域函数

Kotlin 的作用域函数(Scope Functions)是一组高阶函数,主要的作用是简化代码结构、从而提高可读性和可维护性。Kotlin 提供了五种主要的作用域函数:letrunwithapplyalso。每种函数都有其独特的使用场景和返回值,选择合适的作用域函数可以使代码更加简洁优雅。

let 函数

定义和用途

  • 定义let 函数用于在一个对象的上下文中执行代码块,并返回代码块的结果。
  • 用途
    1. 处理可空对象:避免显式的非空检查。
    2. 限制变量作用域:在局部范围内使用变量,避免污染外部作用域。
    3. 链式调用:在链式调用中插入操作步骤。
    4. 转换对象:将对象转换为另一种类型。

基本语法

val result = object.let { 
    // 对 object 进行操作
    it.someOperation()
}
  • object:需要操作的对象。
  • itlet函数的默认参数,代表传入的对象。
  • resultlet函数的返回值。

返回值

let函数返回lambda表达式的最后一行结果,这使得它非常适合在链式调用中使用。

常见用法

处理可空对象

//不使用let函数
kotlin
fun printNameLength(name: String?) {
    if (name != null) {
        println("Name length: ${name.length}")
    }
}

fun main() {
    val name: String? = "Kotlin"
    printNameLength(name)
}

// 使用let函数
fun printNameLength(name: String?) {
    name?.let {
        println("Name length: ${it.length}")
    }
}

fun main() {
    val name: String? = "Kotlin"
    printNameLength(name)
}
  • 简洁性:使用let减少了显式的非空检查,使代码更加简洁。

限制变量作用域

//不使用let函数
fun calculateArea(radius: Double): Double {
    val pi = 3.14159
    val area = pi * radius * radius
    return area
}

fun main() {
    val radius = 5.0
    val area = calculateArea(radius)
    println("Area: $area")
}
// 使用let函数
fun calculateArea(radius: Double): Double {
    return radius.let { r ->
        val pi = 3.14159
        pi * r * r
    }
}

fun main() {
    val radius = 5.0
    val area = calculateArea(radius)
    println("Area: $area")
}

  • 作用域控制:使用let将变量pi的作用域限制在let块内,避免了在整个函数中都可见。

链式调用中的中间操作

//不使用let函数
fun processString(str: String): String {
    val trimmed = str.trim()
    val uppercased = trimmed.toUpperCase()
    return uppercased
}

fun main() {
    val result = processString("  Kotlin  ")
    println(result) // 输出: KOTLIN
}
// 使用let函数
fun processString(str: String): String {
    return str.trim().let { it.toUpperCase() }
}

fun main() {
    val result = processString("  Kotlin  ")
    println(result) // 输出: KOTLIN
}

  • 链式调用:使用let可以在链式调用中插入操作步骤,避免中间变量的显式声明。

转换对象类型

//不使用let函数
fun parseNumber(str: String): Int? {
    val number = str.toIntOrNull()
    if (number != null) {
        return number
    }
    return null
}

fun main() {
    val number = parseNumber("123")
    println(number) // 输出: 123
}
// 使用let函数
fun parseNumber(str: String): Int? {
    return str.toIntOrNull()?.let { it } //返回自身对象
}

fun main() {
    val number = parseNumber("123")
    println(number) // 输出: 123
}

  • 简化逻辑:使用let简化了对可空对象的处理逻辑。

在对象初始化时执行操作

//不使用let函数
data class User(var name: String, var age: Int)

fun createUser(name: String, age: Int): User {
    val user = User(name, age)
    // 执行一些操作
    println("User created: $user")
    return user
}

fun main() {
    val user = createUser("Alice", 30)
}
// 使用let函数
data class User(var name: String, var age: Int)

fun createUser(name: String, age: Int): User {
    return User(name, age).let { 
        // 执行一些操作
        println("User created: $it")
        it
    }
}

fun main() {
    val user = createUser("Alice", 30)
}


注意事项

  • 避免过度使用:虽然let函数非常有用,但过度使用可能导致代码难以理解。应根据具体场景合理使用。
  • 变量命名let函数的默认参数名是it,在需要更明确的语义时,可以显式命名参数。
val length = name?.let { nonNullName ->
    println("Name is $nonNullName")
    nonNullName.length
}

with 函数

定义和用途

  • 定义with 函数用于在特定对象的上下文中执行一段代码块,从而简化代码结构,提高可读性。
  • 用途:适用于对同一个对象执行多项操作,尤其是需要对对象进行配置或批量操作时。

基本语法

with(receiver) {
    // 在这里可以直接访问 receiver 的成员
}
  • receiver:要在其上下文中执行代码块的对象。
  • 代码块内可以直接访问 receiver 的成员,无需重复引用。

常见用法

配置 StringBuilder

// 不使用 with
fun buildStringWithoutWith(): String {
    val stringBuilder = StringBuilder()
    stringBuilder.append("Hello, ")
    stringBuilder.append("World!")
    stringBuilder.append(" Kotlin is awesome.")
    return stringBuilder.toString()
}

// 使用 with
fun buildStringWithWith(): String {
    val stringBuilder = StringBuilder()
    with(stringBuilder) {
        append("Hello, ")
        append("World!")
        append(" Kotlin is awesome.")
    }
    return stringBuilder.toString()
}

初始化一个自定义对象

data class Person(var name: String = "", var age: Int = 0, var address: String = "")
// 不使用 with
fun createPersonWithoutWith(): Person {
    val person = Person()
    person.name = "张三"
    person.age = 25
    person.address = "北京市"
    return person
}

// 使用 with
fun createPersonWithWith(): Person {
    val person = Person()
    with(person) {
        name = "张三"
        age = 25
        address = "北京市"
    }
    return person
}

处理集合数据

// 不使用 with
fun processListWithoutWith(numbers: MutableList<Int>): MutableList<Int> {
    numbers.removeIf { it < 0 }
    numbers.sort()
    numbers.forEach { println(it) }
    return numbers
}

// 使用 with
fun processListWithWith(numbers: MutableList<Int>): MutableList<Int> {
    with(numbers) {
        removeIf { it < 0 }
        sort()
        forEach { println(it) }
    }
    return numbers
}

构建复杂的对象

class Car {
    var brand: String = ""
    var model: String = ""
    var year: Int = 0

    fun startEngine() {
        println("Engine started.")
    }

    fun stopEngine() {
        println("Engine stopped.")
    }
}

// 不使用 with
fun buildCarWithoutWith(): Car {
    val car = Car()
    car.brand = "Toyota"
    car.model = "Camry"
    car.year = 2021
    car.startEngine()
    return car
}

// 使用 with
fun buildCarWithWith(): Car {
    val car = Car()
    with(car) {
        brand = "Toyota"
        model = "Camry"
        year = 2021
        startEngine()
    }
    return car
}

run 函数

定义和用途

  • 定义run 函数结合了 let 和 with 的功能,用于在一个代码块中执行一系列操作,并返回代码块的结果。
  • 用途:适用于对象初始化、配置以及需要计算返回值的场景。

示例

// 不使用 run 作用域函数
val builder = StringBuilder()
builder.append("Hello, ")
builder.append("World!")
builder.append(" Kotlin")
println(builder.toString())

// 使用 run 作用域函数
val builder = StringBuilder()
val st = builder.run {  
	append("Hello, ")  //相当于 builder.append(...)
	append("World!")  
	append(" Kotlin")  
	toString() // 返回结果
}  
println(st)  
}

apply 函数

定义和用途

  • 定义apply 函数主要用于对对象进行初始化或配置,使代码更加简洁和易读。它通过在对象的上下文中执行代码块,并返回该对象本身,从而实现链式调用和简化对象配置。
  • 用途:常用于对象的配置和初始化,特别是在构建对象时设置多个属性。

基本语法

val obj = YourClass().apply {
    // 在这里配置对象的属性或调用方法
}

常见用法

对象初始化

// 不使用 apply 函数
class Person {
    var name: String = ""
    var age: Int = 0
}

fun main() {
    val person = Person()
    person.name = "张三"
    person.age = 25

    println("姓名: ${person.name}, 年龄: ${person.age}")
}

// 使用 apply 函数
class Person {
    var name: String = ""
    var age: Int = 0
}

fun main() {
    val person = Person().apply {
        name = "张三"
        age = 25
    }

    println("姓名: ${person.name}, 年龄: ${person.age}")
}

配置 StringBuilder

// 不使用 apply 函数
val builder = StringBuilder()
builder.append("Hello")
builder.append(", ")
builder.append("World")
builder.append("!")

val result = builder.toString()
println(result)

// 使用 apply 函数
val result = StringBuilder().apply {
    append("Hello")
    append(", ")
    append("World")
    append("!")
}.toString()

println(result)

链式调用

// 不使用 apply 函数
val list = mutableListOf<Int>()
list.add(1)
list.add(2)
list.add(3)
list.add(4)
list.add(5)

println(list)

// 使用 apply 函数
val list = mutableListOf<Int>().apply {
    add(1)
    add(2)
    add(3)
    add(4)
    add(5)
}

println(list)

also 函数

定义与用途

  • 定义also 的主要作用是在链式调用中执行一些附加操作,同时返回原始对象。这使得代码更加简洁和可读。
  • 用途
    1. 对象初始化后执行一些操作:如日志记录、打印调试信息等。
    2. 在链式调用中添加额外的步骤,而不影响原始对象的传递。

常见用法

对象初始化

class Person {
    var name: String = ""
    var age: Int = 0
}
// 不使用 also 函数
fun main() {
    val person = Person()
    person.name = "张三"
    person.age = 25
    println("Person: name=${person.name}, age=${person.age}")
}

// 使用 also 函数
fun main() {
    val person = Person().also {
        it.name = "张三"
        it.age = 25
    }
    println("Person: name=${person.name}, age=${person.age}")
}

链式调用中的附加操作

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

// 不使用 also 函数
fun main() {
    val user = User("李四", 30)
    println("创建用户: $user")
    user.age += 1
    println("用户年龄更新后: $user")
}

// 使用 also 函数
fun main() {
    val user = User("李四", 30).also { 
        println("创建用户: $it")
    }.also {
        it.age += 1
        println("用户年龄更新后: $it")
    }
}
  • 使用 also:通过链式调用,将打印和属性更新操作嵌入到对象创建的过程中,使逻辑更加连贯。

调试和日志记录


// 不使用 also 函数
fun calculateSum(a: Int, b: Int): Int {
    val sum = a + b
    println("计算 $a + $b = $sum")
    return sum
}

fun main() {
    val result = calculateSum(5, 10)
    println("结果是 $result")
}

// 使用 also 函数
fun calculateSum(a: Int, b: Int): Int {
    return (a + b).also { sum ->
        println("计算 $a + $b = $sum")
    }
}

fun main() {
    val result = calculateSum(5, 10).also { 
        println("结果是 $it")
    }
}
  • 使用 also:通过 also 在计算结果的同时进行打印,使代码更为简洁。

集合操作中的 also

// 不使用 also 函数
fun main() {
    val numbers = mutableListOf(1, 2, 3)
    numbers.add(4)
    println("列表内容: $numbers")
}
// 使用 also 函数
fun main() {
    val numbers = mutableListOf(1, 2, 3).also { 
        it.add(4)
        println("列表内容: $it")
    }
}

作用域函数比较

函数 接收者(Receiver) 返回值(Return Value) 常见用法
let it Lambda 的结果 处理非空对象、链式调用
run this Lambda 的结果 对象配置、计算结果
with this Lambda 的结果 操作对象,尤其是多个属性
apply this 对象本身 对象初始化、配置
also it 对象本身 附加操作、日志记录
posted @ 2024-10-19 19:21  Jacob-Chen  阅读(139)  评论(0)    收藏  举报