kotlin - let,run,with,apply,also作用域函数的区别

kotlin - let,run,with,apply,also作用域函数的区别_第1张图片

两个主要区别

  • 上下文对象用this 还是 it
  • 返回值

上下文对象用this 还是 it

this和it是作用域函数获取对象引用的 短名称, 两者都提供相同的功能

使用this的情况

范围函数: run,with,apply 使用this

在大多数情况下,this访问接收器对象的成员时可以省略,从而使代码更短

比如这个例子就是省略了this
data class Person(var name: String, var age: Int = 0, var city: String = "")

fun main() {
    val adam = Person("Adam").apply { 
        age = 20                       // same as this.age = 20 or adam.age = 20
        city = "London"
    }
}

但是不加this,就不容易区分age、city是来自内部接收器Person 还是 外部成员或功能

大家看下不容易区分的情况
data class Person(var name: String, var age: Int = 0, var city: String = "")

var otherName = "wangxueming"

fun main() {
    val adam = Person("Adam").apply { 
        age = 20                       // same as this.age = 20 or adam.age = 20
        city = "London"
        otherName = "wxm"
    }
    println("age = ${adam.age}; city = ${adam.city}; otherName = ${otherName}")
}

这里的otherName你就不容易看出来到底是Person的变量还是外部变量。

使用it的情况

范围函数: let,also使用it

  • it相较this更为简洁
  • 但在范围域内this就不可用了
看范例
import kotlin.random.Random

fun writeToLog(message: String) {
    println("INFO: $message")
}

fun main() {
    fun getRandomInt(): Int {
        return Random.nextInt(100).also {
            writeToLog("getRandomInt() generated value $it")
            //下面这行的this会报编译错误:'this' is not defined in this context
            //writeToLog("getRandomInt() generated value $this")
        }
    }

    val i = getRandomInt()
}

it在lambda中是支持自定义名称的

将上个范例中的getRandomInt(),从it改成value

fun getRandomInt(): Int {
        return Random.nextInt(100).also { value -> {
            writeToLog("getRandomInt() generated value $value")
            writeToLog("getRandomInt() generated 222 value $value")
        }
        }
    }

返回值

apply、also返回上下文对象
let、run、with返回lambda结果

返回 上下文对象

apply、also会返回 上下文对象;
但apply、also发起者是 上下文对象;
这就是个链式调用嘛,准确的是是对fun的链式调用

链式调用就可以这么写了
fun main() {
    val numberList = mutableListOf()
    numberList.also { println("Populating the list") }
        .apply {
            add(2.71)
            add(3.14)
            add(1.0)
        }
        .also { println("Sorting the list") }
        .sort()
    println(numberList)
}

进一步理解一下,in是上下文对象,out依旧是上下文对象
这说明 also和apply不对原先的操作造成任何影响
原来的是fun,添加了also或apply之后,返回的依旧是fun
also、apply可以理解为对原先 fun的补充逻辑处理

我们再看一遍RandomInt的例子
import kotlin.random.Random

fun writeToLog(message: String) {
    println("INFO: $message")
}

fun main() {
    fun getRandomInt(): Int {
        return Random.nextInt(100).also {
            writeToLog("getRandomInt() generated value $it")
        }
    }

    val i = getRandomInt()
}

添加also之后,Random.nextInt(100)不受影响,只是额外打印了一行log
这就可以动态的 给fun添加一个原先fun处理完成后的逻辑

返回lambda表达式结果

let、run、with会返回 lambda表达式结果
这就提供了 将fun计算的结果传给下一个函数继续计算
这是对结果的链接调用

看个统计末尾 字母为e个数的范例
fun main() {
    val numbers = mutableListOf("one", "two", "three")
    val countEndsWithE = numbers.run { 
        add("four")
        val Res1 = count { it.startsWith("f") }
        println("Res1 = ${Res1}")
        add("five")
        val Res2 = count { it.startsWith("f") }
        println("Res2 = ${Res2}")
        println("${numbers}")
        count { it.endsWith("e")&&it.startsWith("t") }
    }
    println("There are $countEndsWithE elements that end with e.")
}

输出结果为

Res1 = 1
Res2 = 2
[one, two, three, four, five]
There are 1 elements that end with e.

正如文章开头说的,作用于表达式 只是书写的简化
相当于是 多行 逻辑处理的整合
每一行代码都能依次执行,并及时的得到相应的结果


函数选择

功能 对象参考 返回值 是扩展功能 适用情况
let it Lambda结果 仅支持在非null对象上使用;将 表达式 像变量一样引入到 作用域中
run this Lambda结果 对象配置和计算结果
run Lambda结果 否:在没有上下文对象的情况下调用 需要表达式的运行语句
with this Lambda结果 否:将上下文对象作为参数。 对对象进行分组功能调用
apply this 上下文对象 对象配置
also it 上下文对象 附加功能

你可能感兴趣的:([kotlin])