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

Kotlin作用域函数

Kotlin 提供了一系列用来在给定对象上下文中执行代码块的函数,包括let、with、run、apply、also。每个库函数都有它的实际应用场景,使用它们能让你的代码会更具有可读性、更优雅、更简洁。善于合理使用标准库函数,也是衡量对Kotlin掌握程度标准之一。 下面是每个函数的基本用法和适用场景,最后对他们进行对比总结以及在实际编码中该如何选择哪个函数。

let

let函数只接收一个Lambda参数,并且会在Lambda表达式中提供调用对象的上下文it。返回值为函数块的最后一行或指定return表达式。

底层内联结构:

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

语法结构:

 val result = obj?.let { 
	// it指代obj的上下文
 	it.toString()
    // let函数的返回值 
 }

let的适用场景:

使用let函数处理需要针对一个可null的对象统一做判空处理。

with

with函数接收两个参数: 第一个参数可以是一个任意类型的对象, 第二个参数是一个Lambda表达式。

with函数会在Lambda表达式中提供第一个参数对象的上下文,并使用Lambda表达式中的最后一行代码或return语句作为返回值返回。

底层内联结构:

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

语法结构:

 val result = with(obj) { 
    // 这里是obj的上下文 "value" 
    // with函数的返回值 
 }

with的适用场景:

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

例:

with(item) {
	holder.tvTitle.text = "标题: $title"
	holder.tvDescription = "描述: $description"
}

在with块中可以直接访问item的实例的公有属性和方法。

run

run函数的用法和使用场景其实和with函数是非常类似的,只是稍微做了一些语法改动而已。

首先run函数是不能直接调用的,而是一定要调用某个对象的run函数才行;

其次run函数只接收一个Lambda参数,并且会在Lambda表达式中提供调用对象的上下文。

其他方面和with函数是一样的,包括也会使用Lambda表达式中的最后一行代码或者return表达式作为返回值返回,run函数实际上可以说是let和with两个函数的结合体。

底层内联结构:

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

语法结构:

val result = obj.run { 
    // 这里是obj的上下文 
    "value" // run函数的返回值 
}

run的适用场景:

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

item?.run {
	holder.tvTitle.text = "标题: $title"
	holder.tvDescription = "描述: $description"
}

apply

apply函数和run函数也是极其类似的,都是要在某个对象上调用,并且只接收一个Lambda参数,也会在Lambda表达式中提供调用对象的上下文,

但是apply函数无法指定返回值,而是会自动返回调用对象本身。代码块中上下文对象是this

底层内联结构:

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

语法结构:

val result = obj.apply { 
    // 这里是obj的上下文 
}
//result是obj对象本身

apply的适用场景:

整体作用功能和run函数很像,唯一不同点就是它返回的值是对象本身,而run函数是一个闭包形式返回,返回的是最后一行的值。正是基于这一点差异它的适用场景稍微与run函数有点不一样。apply一般用于一个对象实例初始化的时候,需要对对象中的属性进行赋值。或者动态inflate出一个XML的View的时候需要给View绑定数据也会用到,这种情景非常常见。特别是在我们开发中会有一些数据model向View model转化实例化的过程中需要用到。

例:

mSheetDialogView = View.inflate(activity, R.layout.layout_sheet, null).apply{
   isFakeBoldText = true
   max = 10
   progress = 0
}

also

also函数的结构实际上和let很像唯一的区别就是返回值的不一样,let是以闭包的形式返回,返回函数体内最后一行的值,如果最后一行为空就返回一个Unit类型的默认值。而also函数返回的则是传入对象的本身

底层内联结构:

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

语法结构:

val result = obj.also { 
    // it是obj的上下文 
}
//result是obj对象本身

also的适用场景:

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

例:

items.apply {
	//初始化配置
}.also {
	//附加操作
}.forEach {
	//it.something
}

总结对比

函数名函数体内对象返回值是否是扩展函数适用场景
letit指代当前对象Lambda表达式最后一行或return语句适用于处理不为null的操作场景
withthis指代当前对象或者省略Lambda表达式最后一行或return语句适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法
runthis指代当前对象或者省略Lambda表达式最后一行或return语句适用于let,with函数任何场景。
applythis指代当前对象或者省略上下文对象(自身)1、适用于run函数的任何场景,一般用于初始化一个对象实例的时候,操作对象属性,并最终返回这个对象。
2、动态inflate出一个XML的View的时候需要给View绑定数据也会用到。
3、一般可用于多个扩展函数链式调用。
4、数据model多层级包裹判空处理的问题。
alsoit指代当前对象上下文对象(自身)适用于let函数的任何场景,一般可用于多个扩展函数链式调用

如何选择?

要选择正确的函数, 请考虑以下几点:
是否在块中的多个对象上调用方法, 或者将上下中对象的实例作为参数传递? 如果是, 那么使用以it 而不是 this 形式访问上下文对象的函数之一 ( also 或 let ) 。 如果在代码块中根本没有用到接收者, 那么使用also。下面是一张来自其他博客的标准函数选择图:

通过这张图可以更加清晰的知道该如何选择使用哪个函数。

其他注意点:

  • 建议尽量不要使用多个标准库函数进行嵌套,不要为了简化而去做简化,否则整个代码可读性会大大降低,一会是it指代,一会又是this指代,估计隔一段时间后连自己都不知道指代什么了。
  • let、with和run函数之所以能够返回其他类型的值,其原理在于lambda表达式内部返回最后一行表达式的值,所以只要最后一行表达式返回不同的对象,那么它们就返回不同类型,表现上就是返回其他类型。
  • 关于T.also和T.apply函数为什么都能返回自己本身,是因为在各自Lambda表达式内部最后一行都调用return this,返回它们自己本身,这个this能被指代调用者,是因为它们都是扩展函数特性。

我用hexo搭建了一个自己的博客 https://xiayunquan.gitee.io/blog, 感兴趣的朋友可以去看看。
分类、标签和搜索功能使得可以更好的管理和查阅文章,而且还不用看烦人的广告。
后期我还是会坚持同步更新个人博客和CSDN的。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值