Kotlin内置函数之 let 、also、apply、run、with

前言

在Kotlin中的源码标准库(Standard.kt)中提供了一些Kotlin扩展的内置函数可以优化kotlin的编码。Standard.kt是Kotlin库的一部分,它定义了一些基本inline函数。这些内置函数可以优化kotlin的编码,更优雅,更有效率的实现我们的功能。

内联函数之with

我们先来看下这个函数实现

/**
 * Calls the specified function [block] with the given [receiver] as its receiver and returns its result.
 *
 * For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#with).
 */
@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    
    
    contract {
    
    
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
}

通过注释和代码我们可以看出看出with函数是接收了两个参数,分别为T类型的对象receiver和一个lambda函数块,返回值结果是函数执行的结果。但它不是以扩展的形式存在的,它是将某对象作为函数的参数,在函数块内可以通过 this 指代该对象,this可以省略。

使用姿势

var people = People("zg",20,165)
val result = with(people, {
    
    
   println("name is $name, age is $age , height is $height")
   1000
})
//或者 由于with函数最后一个参数是一个函数,可以把函数提到圆括号的外部
val result = with(people) {
    
    
   println("name is $name, age is $age , height is $height")
   1000
}
println(result)

打印结果:
name is zg, age is 20, height is 165
1000

应用场景

with适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法即可。经常用于Android中RecyclerView中onBinderViewHolder中,数据model的属性映射到UI上。

override fun onBindViewHolder(holder: ViewHolder, position: Int){
    
    
   val item = getItem(position)?: return
   with(item){
    
    
      holder.name.text = name
	  holder.age.text = age
	  holder.height.text = height
      ...   
   }
}

内联扩展函数之 let 和 also

我们先来看下这两个函数的实现

/**
 * Calls the specified function [block] with `this` value as its argument and returns its result.
 *
 * For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#let).
 */
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
    
    
    contract {
    
    this ContracBuilder)
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}
/**
 * Calls the specified function [block] with `this` value as its argument and returns `this` value.
 *
 * For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#also).
 */
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.also(block: (T) -> Unit): T {
    
    
    contract {
    
    this ContracBuilder) 
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block(this)
    return this
}

看到这两个内置函数的实现,你大概知道为什么我要把它们俩放一起了。可以看到这两个inline函数的注释和实现可以说几乎是相同的。唯一不同的是let函数returns its result(返回函数体的返回),而also函数则是returns this value(返回调用函数者)。

使用姿势

首先来看let,在函数体内用it代替调用者

var result = "test".let {
    
    
    println(it.toString())
    println(it.indexOf("s"))
    1000  //最后一行结果返回
}
println(result)

打印结果:
test
2
1000

接下来看also,在函数体内用it代替调用者

//返回结果为调用者
var result = "test".also {
    
    
	println(it.toString())
    println(it.indexOf("s"))
    1000
}
println(result)

打印结果:
test
2
test

可以看到两者除了返回结果之外,其他的都是差不多的。那么这两者应用场景分别是什么了?

应用场景

let扩展函数的实际上是一个作用域函数,当你需要去定义一个变量在一个特定的作用域范围内,let函数的是一个不错的选择;let函数另一个作用就是可以避免写一些判断null的操作。

场景一: 最常用的场景就是使用let函数处理需要针对一个可null的对象统一做判空处理。

obj?.setXXX1()
obj?.setXXX2()
obj?.setXXX3()
...

使用let函数:
obj?.let {
    
    
	it.setXXX1()
	it.setXXX2()
	it.setXXX3()
	...
}

场景二: 明确一个变量所处特定的作用域范围内可以使用。

also适用于let函数的任何场景,also函数和let很像,只是唯一的不同点就是let函数最后的返回值是最后一行的返回值而also函数的返回值是返回当前的这个对象,一般可用于多个扩展函数链式调用。

var people = People("zg",20,165)
println(people)
//链式调用
var result = people.also {
    
    
   it.name = "zg1"
}.also {
    
    
   it.height = 175
}.also {
    
    
   it.age = 25
}
println(result)

打印结果:
People(height=165, name=zg, age=20)
People(height=175, name=zg1, age=25)

内联扩展函数之apply和 run

我们先来看下这两个函数的实现

/**
 * Calls the specified function [block] with `this` value as its receiver and returns `this` value.
 *
 * For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#apply).
 */
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
    
    
    contract {
    
    
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}

/**
 * Calls the specified function [block] with `this` value as its receiver and returns its result.
 *
 * For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#run).
 */
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
    
    
    contract {
    
    
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}

通过查看注释和源码实现发现其实这两个函数也有很多共同点。apply返回的是调用者,而run返回的是闭包的最后一行。同时两者在函数体中都用this来代替调用者,this并且可以省略。

使用姿势

首先看apply,在函数体内用this替代调用者,this可省略。

var result = "test".apply {
    
    
   println(toString()/*this.toString()*/)
   1000
}
println(result)//返回调用者

打印结果:
test
test

接下来看run,在函数体内用this替代调用者,this可省略。

var result = "test".run{
    
    
   println(toString()/*this.toString()*/)
   1000
}
println(result)//返回函数体闭包中的最后一行

打印结果:
test
1000

同样,出了返回结果又差异之外,其他差不多。

使用场景

apply一般用于一个对象实例初始化的时候,需要对对象中的属性进行赋值。或者动态inflate出一个XML的View的时候需要给View绑定数据也会用到,这种情景非常常见。

var people = People().apply {
    
    
	name = "xxx"
	age = 20
	height = 175
	...
}

run适用于let,with函数任何场景。因为run函数是let,with两个函数结合体,准确来说它弥补了let函数在函数体内必须使用it参数替代对象,在run函数中可以像with函数一样可以省略,直接访问实例的公有属性和方法,另一方面它弥补了with函数传入对象判空问题,在run函数中可以像let函数一样做判空处理。

let,with,run,apply,also函数区别

通过以上几种函数的介绍,可以很方便优化kotlin中代码编写,整体看起来几个函数的作用很相似,但是各自又存在着不同。使用的场景有相同的地方比如run函数就是let和with的结合体。下面一张表格可以清晰对比出他们的不同之处。
在这里插入图片描述

尾巴

今天的笔记就这么多了,老规矩,喜欢我的文章,欢迎素质三连:点赞,评论,关注,谢谢大家!

猜你喜欢

转载自blog.csdn.net/abs625/article/details/109074104