Kotlin的作用域函数 let、also、with、run、apply

作用域函数主要有下面这几种,apply ,with  、run 、let 、以及 also 。这些函数非常类似,它们的主要区别:

  • 引⽤上下⽂对象的⽅式 (this / it)
  • 返回值

他们在开发中的使用场景主要有两个,一是非空判断,二是对象的初始化或者本身及方法的频繁调用。


apply函数

//java
val list = ArrayList<String>()
list.add("A")
list.add("B")      
list.add("C")    
  
         
//kotlin
val list = ArrayList<String>()
list.apply{
 add("A")
 add("B")
 add("C")
}

//和上面效果相同,一般省略this
val list = ArrayList<String>()
list.apply{
  this.add("A")
  this.add("B")
  this.add("C")
}

with函数

val list = ArrayList<String>()
with(list){
 add("A")
 add("B")
 add("C")
}

run函数

val list = ArrayList<String>()
list.run{
 add("A")
 add("B")
 add("C")
}

let函数

// 使用Java
if(mList!=null){
mList.add("A")
mList.add("B")      
mList.add("C") 
}

// 使用kotlin(无使用let函数)
mList?.add("A")
mList?.add("B")      
mList?.add("C") 

// 使用kotlin(使用let函数)
// 方便了统一判空的处理 & 确定了mVar变量的作用域
mList?.let {
      it.add("A")
      it.add("B")      
      it.add("C") 
}

also函数

/ let函数
var result = list.let {
               it.add("A")
               it.add("B")
               it.add("C")
               3
}
// 最终结果 = 返回3给变量result

// also函数
val list = ArrayList<String>()
var result =list.also{
              it.add("A")
              it.add("B")
              it.add("C")
              3
}

// 最终结果 = 返回一个list对象给变量result

返回值

根据返回结果,作⽤域函数可以分为以下两类:

  • apply 及 also 返回上下⽂对象。
  • let 、run 及 with 返回 lambda 表达式结果.

上下文对象

apply 及 also 的返回值是上下⽂对象本⾝。因此,它们可以作为辅助步骤包含在调⽤链中:你可以 继续在同⼀个对象上进⾏链式函数调⽤。

val numberList = mutableListOf<Double>()
numberList.also { println("Populating the list") }
.apply {
add(2.71)
add(3.14)
add(1.0)
}
.also { println("Sorting the list") }
.sort()

Lambda 表达式结果

let 、run 及 with 返回 lambda 表达式的结果。所以,在需要使⽤其结果给⼀个变量赋值,或者在需 要对其结果进⾏链式操作等情况下,可以使⽤它们。

val numbers = mutableListOf("one", "two", "three")
val countEndsWithE = numbers.run {
add("four")
add("five")
count { it.endsWith("e") }
}
println("There are $countEndsWithE elements that end with e.")

使用技巧

为了帮助你为你的场景选择合适的作⽤域函数,我们会详细地描述它们并且提供⼀些使⽤建议。从技术 ⻆度来说,作⽤域函数在很多场景⾥是可以互换的,所以这些⽰例展⽰了定义通⽤使⽤⻛格的约定⽤ 法。

apply

上下⽂对象 作为接收者( this )来访问。返回值 是上下⽂对象本⾝。 对于不返回值且主要在接收者( this )对象的成员上运⾏的代码块使⽤ apply 。apply 的常⻅情况 是对象配置。这样的调⽤可以理解为“将以下赋值操作应⽤于对象”。

val adam = Person("Adam").apply {
age = 32
city = "London"
}
println(adam)

将接收者作为返回值,你可以轻松地将 apply 包含到调⽤链中以进⾏更复杂的处理。

with

⼀个⾮扩展函数:上下⽂对象作为参数传递,但是在 lambda 表达式内部,它可以作为接收者( this )使 ⽤。返回值是 lambda 表达式结果。 我们建议使⽤ with 来调⽤上下⽂对象上的函数,⽽不使⽤ lambda 表达式结果。在代码中,with 可 以理解为“对于这个对象,执⾏以下操作。”

val numbers = mutableListOf("one", "two", "three")
with(numbers) {
println("'with' is called with argument $this")
println("It contains $size elements")
}

run

上下⽂对象 作为接收者( this )来访问。返回值 是 lambda 表达式结果 当 lambda 表达式同时包含对象初始化和返回值的计算时,run 很有⽤。

val service = MultiportService("https://example.kotlinlang.org", 80)
val result = service.run {
port = 8080
query(prepareRequest() + " to port $port")
}
// 同样的代码如果⽤ let() 函数来写:
val letResult = service.let {
it.port = 8080
it.query(it.prepareRequest() + " to port ${it.port}")
}

let

上下⽂对象作为 lambda 表达式的参数( it )来访问。返回值是 lambda 表达式的结果。 let 可⽤于在调⽤链的结果上调⽤⼀个或多个函数。例如,以下代码打印对集合的两个操作的结果:

val numbers = mutableListOf("one", "two", "three", "four", "five")
val resultList = numbers.map { it.length }.filter { it > 3 }
println(resultList)

使⽤ let ,可以写成这样:

val numbers = mutableListOf("one", "two", "three", "four", "five")
numbers.map { it.length }.filter { it > 3 }.let {
println(it)
// 如果需要可以调⽤更多函数
}

also

上下⽂对象作为 lambda 表达式的参数( it )来访问。返回值是上下⽂对象本⾝。 also 对于执⾏⼀些将上下⽂对象作为参数的操作很有⽤。对于需要引⽤对象⽽不是其属性与函数的 操作,或者不想屏蔽来⾃外部作⽤域的 this 引⽤时,请使⽤ also 。 当你在代码中看到 also 时,可以将其理解为“并且⽤该对象执⾏以下操作”。

val numbers = mutableListOf("one", "two", "three")
numbers
.also { println("The list elements before adding new one: $it") }
.add("four")

介绍下lambda表达式

lambda表达式实际上有两种形式,一种是基本形式,还有一种是带接收者的形式,两种lambda表达式如下所示:

普通lambda表达式:{ () -> R }  ,                            即函数无入参,返回值为R类型。

带接收者的lambda表达式:{ T.() -> R }                    即申明一个T类型的接收者对象,且无入参,返回值为R类型。

Kotlin中的拓展函数,实际上就是使用的带接收者的lambda表达式,

带接收者的lambda与普通的lambda的区别主要在于this的指向区别,T.() -> R里的this代表的是T的自身实例,而() -> R里,this代表的是外部类的实例。

apply函数的inline+lambda结构

@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }

with函数的inline扩展函数+lambda结构

@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()

可以看出with函数是接收了两个参数,分别为T类型的对象receiver和一个lambda函数块,所以with函数最原始样子如下:

val result = with(user, {
        println("my name is $name, I am $age years old, my phone number is $phoneNum")
        1000
    })

但是由于with函数最后一个参数是一个函数,可以把函数提到圆括号的外部,所以最终with函数的调用形式如下:

val result = with(user) {
        println("my name is $name, I am $age years old, my phone number is $phoneNum")
        1000
    }

run函数的inline+lambda结构

@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R = block()

let函数inline扩展函数+lambda结构

@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R = block(this)

also函数的inline+lambda结构

@kotlin.internal.InlineOnly
@SinceKotlin(“1.1”)
public inline fun T.also(block: (T) -> Unit): T { block(this); return this }

参考资料

https://juejin.im/entry/5e5321ef5188254935091e25

https://blog.csdn.net/u013064109/article/details/78786646?utm_medium=distribute.pc_relevant.none-task-blog-OPENSEARCH-2.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-OPENSEARCH-2.nonecase

猜你喜欢

转载自blog.csdn.net/jingerlovexiaojie/article/details/106480554