写点什么

Kotlin 作用域函数 [Scope Function](下)

作者:子不语Any
  • 2022-11-30
    湖南
  • 本文字数:1896 字

    阅读完需:约 6 分钟

Kotlin作用域函数[Scope Function](下)

前言

上一篇文章介绍过 Kotlin 中作用域函数,并以函数run为例,介绍作用域函数的特点和使用。除run之外,Kotlin 标准库还内置了letapplyalso等几个常用作用域函数,下面介绍它们用法和使用场景。

概览

1. 常见作用域函数模板化结构

runletapplyalso,4 个作用域函数,其实是 2 个特性的组合结果:


  • 调用作用域函数的对象,是作为this传入,还是作为唯一参数(默认为it)传入;

  • 作用域函数的返回值,是调用的对象,还是 lambda 的返回值。


用伪代码来解释一下:


val result = object.xxx { param ->    val lambdaResult = param.toString()    lambdaResult}
复制代码


xxx可以是runletapplyalso其中一个。


如果xxx = run/let,那么 result == lambdaResult;而如果xxx = apply/also,那么 result == object。且lambdaResult这一行会报错,因为applyalso 的 lambda 返回值必须是 Unit。


lambda 最后一行的值会作为 lambda 的返回值。它等价于 return@xxx lambdaResult@xxx表示返回的是这个 lambda,而不是退出整个上层方法。


如果xxx = let/also,那么param == object;而如果xxx = run/apply的话,那么 lambda 是个无参数 lambda,不存在param param -> 会报错;


如果 lambda 为单参数 lambda,此时param ->可以省略,Kotlin 提供默认的单参数名it


总结成表就是:



快速记忆:4 个常用作用域函数分别是 2 个特性的两两组合,不需要专门记忆。

2. run/let/apply/also 使用场景举例

已经知道这 4 个作用域函数的特点,那么怎么用好它们呢?下面一起来看下这几个作用域函数的使用场景。

2.1 run 函数

这是项目中的一段代码:


mRecordViewHelper?.run {    mEffectsView.visibility = View.INVISIBLE    mLyricViewer.visibility = View.INVISIBLE    mStatusView = AudioRecordStatusView(CommonContext.getApplicationContext(), null)    enableSoloCountBackView()}
复制代码


run方法很适合用在对某个对象做一系列操作。可空对象mRecordViewHelper使用run方法转换成非空对象this传入到 lambda 中,并在 lambda 内部进行一系列的赋值和方法调用。run的返回值是 lambda 的最后一行结果,在这个示例是Unit。使用 lambda 结果的用例较少,主要就是这种转换为this的用法。

2.2 let 函数

使用run还是使用let更像是编码习惯问题,因为有些人不习惯把调用对象转换成this,而更喜欢用入参it


使用run还是使用let的确没有太大区别。不过这几种情况,更建议使用let


  • 调用对象主要作为参数,而不是用于初始化或方法调用时。


// 赋值和调用的方法都是类的成员和方法,不是 mRecordViewHelper 的成员和方法mRecordViewHelper?.run {    myFirstView = firstView    mySecondView = secondView    setTargetView(targetView)}
// 使用 let,不会存在两个 this 混用,代码可读性更高。mRecordViewHelper?.let { myFirstView = it.firstView mySecondView = it.secondView setTargetView(it.targetView)}
复制代码


  • 当 lambda 中需要用到类的this时;


// 因this 的变化,导致引用类的 this 需要加@MainActivity 这和 Java匿名类 MainActivity.this 效果一样mRecordViewHelper?.run {    setOnClickListener(this@MainActivity)}
// let 不会改变 this,在需要引用类的 this 的时候比较方便。mRecordViewHelper?.let { it.setOnClickListener(this)}
复制代码


总结一下:


  • 当 lambda 主要执行的是调用对象的方法和赋值时,建议使用run

  • 而当调用对象主要用作参数时,建议使用let

  • 当 lambda 会用到类的this时,建议使用let

2.3 apply 函数

applyrun的区别主要在于,apply返回的是调用对象。这个特性使得apply很适合用来做初始化的工作。如:


class VideoConfig {    val profile = VideoEncodeProfile().apply {// 初始化数值        audioBitRate = 44100        videoWidth = 480        videoHeight = 480        videoFrameRate = 30        setDefaultQuality()    }}
复制代码


apply 很适合用来做 property 的初始化,这样 property 的初始化代码就不用写在 init 块里,做到了代码的高内聚。

2.4 also 函数

also 的适用场景,和runlet差不多,是与apply来做对比的。具体建议也和runlet类似:


  • 当 lambda 主要执行的是调用对象的方法和赋值时,建议使用apply

  • 而当调用对象主要用作参数时,建议使用also

  • 当 lambda 会用到类的this时,建议使用also

3. 更多的作用域函数

在 Kotlin 中,除了runletapplyalso之外,还有其他高效的作用域函数。但另一方面掌握了这 4 个作用域函数,已经覆盖大部分使用场景。剩下的几个使用需求没有那么的迫切,但能掌握是更好咯,可以帮助写出更有 Kotlin 样的代码。

发布于: 2022-11-30阅读数: 29
用户头像

子不语Any

关注

If not now,when? 2022-09-17 加入

安卓攻城狮

评论

发布
暂无评论
Kotlin作用域函数[Scope Function](下)_android_子不语Any_InfoQ写作社区