如何选择Kotlin作用域函数-let,run,with,apply

⏰ : 全文字数:2326+

🥅 : 内容关键字:作用域函数,kotlin,let,run,apply

🤙 : 公_众_号:七郎的小院

🥅 : 更多文章:blog.softgeek.cn

如何选择这些作用域函数

在Kotlin开发中,经常使用到let,apply,also,run等一些作用域函数,具体什么是作用域函数,可以看下官网地址作用域函数,这里不在展开讲,不明白的可以看下官方文档。

我们在实际使用中,你会是否会有这样的疑惑,到底 使用那个作用域函数,好像有些情况,每个都可以。有 些情况,又只能用特定的函数?

其实要弄明白这些,就需要弄明白这些函数的特点。而要区分这些函数的差异,我们可以从三个维度来进行区分,分别是:是否是扩展函数、返回值差异、参数是什么。可以看下下面的表格:

函数 对应引用方式 返回值 是否是扩展函数
let it Lambda result Yes
run this Lambda result Yes
run - Lambda result No: called without the context object
with this Lambda result No: takes the context object as an argument.
apply this Context object Yes
also it Context object Yes

上面的表格是官网中的表格,列举了各个函数在各个维度的差异。但是看起来还是觉得不够直观,所以这里我做一个选择使用那个函数的 "抉择图",可以看下

所以下次不知道用哪个,就可以照着这个图选就行了~~~

使用问题

现在kotlin基本上是开发的主力语言,开发中也是大量使用到作用域函数。但是使用作用域函数的时候,碰到了一些比较典型问题。这些问题在review其他人代码时也会看到,说明大家可能都碰到了这些问题,这里选取最常见的两个进行说明。

1、过多嵌套使用

经常看到代码中有很多嵌套使用作用域函数的场景,举个例子:

kotlin 复制代码
    fun getUserFriendsInfo() {
        userId?.let {
            val userInfo = getUserInfo(it)
            userInfo?.apply {
                val friends = getFriendsByUserInfo(userInfo)
                friends?.apply {
                    ......
                }
            }
        }
    }

从上面的代码可以看到,如果过多使用嵌套作用域函数,就会形成类似"回调地狱"的代码形式,再加上代码中还用到了一些lambdas,情形更加严重,导致代码极其难看。在我们的项目中,经常看到类似的代码。那么有什么办法改善呢?

我认为有如下几个方法:

  • 一个是把代码进行抽离,放到不同的函数中,减少嵌套
  • 很多情况都是为了判空,我们可以提前做空判断,或者定义Contract,后续的该变量都是用非空状态,这样就不用到处判断,减少嵌套

2、参数this/it、引用混淆

有些作用域函数是有参数传递的,有些是this,有些是it,而如果嵌套使用,就会出现代码可读性的问题,甚至引起错误,举几个例子:

第一个例子:

kotlin 复制代码
    fun getUserFriendsInfo() {
        userId?.let {
            val userInfo = getUserInfo(it)
            userInfo?.let {
                val friends = getFriendsByUserInfo(it)
                friends?.let {
                    println(it)
                    ......
                }
            }
        }
    }

上面代码,可以看到有三个it,到底是那个是哪个,有时候并不一定好区分。同样其他参数是it的作用域函数也有这个问题。

第二个例子:

kotlin 复制代码
class Style() {
    var width = 0.0
    var height = 0.0
    var name = ""
}

class Widget(val name: String, val width: Double) {
    val style: Style
    
    init {
        val height = width * 1.5
        style = Style().apply {
            width = width
            name = name
        }
    }
}

上面代码中,apply方法里面。Widget的width、name和Style的同名,有时候就无法分辨清楚,甚至报错。 这些情况该怎么处理呢?

  • 不要使用默认的命名,比如it,尽量重命名有意义的名字
  • 不要大量嵌套使用作用域函数
  • 像apply这种没有办法重命名参数的作用域函数,在引用变量的时候,可以借助label标签,比如上面apply的例子
kotlin 复制代码
class Widget(val name: String, val width: Double) {
    val style: Style

    init {
        val height = width * 1.5

        style = Style().apply {
            // 使用标签
            this.width = this@Widget.width 
            this.height = height
            this.name = this@Widget.name
        }
    }
}
相关推荐
深圳老胡2 小时前
STM32C5A3ZG 网络调试总结:从启动失败到连续 Ping 稳定
笔记·stm32·单片机·代码规范
TimeFine4 小时前
AI 编码工作流的设计
android·ai编程
骑着蜗牛撵大象3274 小时前
C++20 协程 × 屏障:手写一个 co_await 版 Barrier,让等待不再钉死线程
android·c++20·协程·并发编程·co_await·屏障同步
山东孙燕姿6 小时前
设备台账重构:从一张表到三层模型
android·java·网络·低代码·重构·rxjava
蒸鱼Yuzheng7 小时前
移动端 CPU 调度分析:线程亲和性、大小核迁移与卡顿
android·perfetto·性能分析·线程优化·cpu调度
booboosui7 小时前
安卓唤醒词:12 维 MFCC 加 DTW,阈值 1.8
android
传奇开心果7 小时前
Compose Multiplatform 共享边界实战:架构契约、互操作同步与可回退设计
android·kotlin
事圆则缓8 小时前
从开机到首帧:Android 系统与应用启动流程源码分析
android
AI吃大瓜8 小时前
指针压力表读数4:Android实现指针压力表读数(可实时检测)
android·指针读数·指针压力表·压力表·压力表读数
fengci.8 小时前
CTF2刷题记录(困难部分)
android·开发语言·前端·学习·php