快速学习Kotlin(八)作用域函数

作用域函数是什么?

他是Kotlin内置的,可以对数据进行一系列变换的函数。他们与集合的操作符非常相似,但是集合操作符只能用于集合的一些变换,而作用域函数可以作用于所有对象,他可以对所有对象进行一系列的操作。

在kotlin中作用域函数总共有五种,run、with、let、apply、also;我们一一来看他们的作用。

run

    var sex:String = "man";

    run {
        var sex:String = "women";
        print(sex);     //输出women
    }

    print(sex);     //输出man

可以看到,run拥有自己的作用域,在被run大括号包裹起来的内容里面我们重新定义了性别这个sex变量,并且这个变量只用在run这个作用域里面,离开了这个作用域这个变量便失效了。

目前对于这个run函数看起来貌似没有什么用处,但是在run函数当中它不仅仅只是一个作用域,他还有一个返回值。他会返回在这个作用域当中的最后一个对象。

例如现在有这么一个场景,用户领取app的奖励,如果用户没有登录弹出登录dialog,如果已经登录则弹出领取奖励的dialog。我们可以使用以下代码来处理这个逻辑。

    run {
    
        if (islogin) loginDialog else getAwardDialog
    
          
    }.show()

这样根据if执行的不同语句,会返回不同的Dialog对象,然后根据返回不同的Dialog对象我们可以进行不同的操作。

with和其它通用扩展函数

在这里之所以将with函数单独拿出来进行说明,是因为with得用法和其它通用的扩展函数的用法比较独特。在这里我们依然使用run函数来进行对比。对于下面这段代码做的是同样一件事。它们的不同之处就是一个使用了with(T)函数,而另一个则是使用了T.run函数。

with(webView.settings){
    javaScriptEnabled = true
    databaseEnabled = true
}

webView.settings.run { 
    javaScriptEnabled = true
    databaseEnabled = true
}

乍一看,其实没啥区别,感觉都一样。但是想象一个场景,假设webview为空的时候我们应该怎么处理?我们来改造一下这种场景的代码,


with(webview.settings) {
    this?.javaScriptEnabled = true
    this?.databaseEnabled = true
}


webview.settings?.run {
    javaScriptEnabled = true
    databaseEnabled = true
}

这么以来就很明显了,当然是T.run方法会更好,因为我们可以在使用这些函数之前可以进行对null的检查。

对于with也是存在一个返回值,它也是会返回在这个作用域当中的最后一个对象。

let

T.let与T.run两个函数十分相似,我们下面来进行比较来学习一下。

    var stringVariable:String = "aa";

    stringVariable?.run {
        println("字符串的长度为$this.length")
    }

    stringVariable?.let {
        println("字符串的长度为 ${it.length}")
    }
    

在这两段代码中可以清晰的看到。在T.run函数中通过this来获取stringVariable对象,而在T.let函数中通过it来取出stringVariable对象。当然我们也能够为it重新命名。如果我们不想覆盖外部作用域的this,这时候去使用T.let会更加的方便。

从上面可以看出,T.run 好像比 T.let 高级,因为它更隐式一些,但是 T.let 函数会有些一些微妙的优势:

  • T.let 可以更清楚地区分所得变量和外部类的函数/成员。
  • this 不能被省略的情况下,例如用作一个函数参数,it 比 this 更短更清晰。
  • T.let 允许用更好的命名来表示转换过的所用变量,也就是说,你可以把 it 转换为其他名字:
stringVariable?.let {
    nonNullString ->
    println("The non null string is $nonNullString")
}

also

在这些作用域中它们都会存在一个返回值。在上面的讲述的run,with,T.run,T.let中它们返回的都是作用域中最后一个对象。当然它们所返回的值是允许和接受者it或者this对象的类型不同。但是并不是所有的扩展函数都是返回作用域的最后一个对象。例如T.also函数。

val original = "abc"

original.let {
    println("The original String is $it") // "abc"
    it.reversed() 
}.let {
    println("The reverse String is $it") // "cba"
    it.length  
}.let {
    println("The length of the String is $it") // 3
}


original.also {
    println("The original String is $it") // "abc"
    it.reversed() 
}.also {
    println("The reverse String is ${it}") // "abc"
    it.length  
}.also {
    println("The length of the String is ${it}") // "abc"
}

从上面两段代码可以看出T.let和T.also的返回值使不同的。T.let返回的是作用域中的最后一个对象,它的值和类型都可以改变。但是T.also不管调用多少次返回的都是原来的original对象。

对于T.let和T.also都能够进行链式操作,那么我们现在结合一下T.let和T.also的链式调用来看一下在实际场景中的应用。

//原始函数
fun makeDir(path: String): File  {
    val result = File(path)
    result.mkdirs()
    return result
}

//通过let和also的链式调用改进后的函数
fun makeDir(path: String) = path.let{ File(it) }.also{ it.mkdirs() }

apply

到目前为止除了T.apply没有使用到以外,根据上面的用法我们可以总结出来这些扩展函数的三大特性。

  • 它们都有自己的作用域
  • 它们作用域中的接收者是this或者it
  • 它们都有一个返回值,返回最后一个对象(this)或者调用者自身(itself)

由此可想到对于T.apply无非也就是这三个特性。对于T.apply它作用域中的接收者是this,并且返回的调用者T。因此,T.apply的其中一个使用场景可以用来创建一个Fragment,代码如下所示:

// 使用普通的方法创建一个Fragment
fun createInstance(args: Bundle) : MyFragment {
    val fragment = MyFragment()
    fragment.arguments = args
    return fragment
}

// 通过apply来改善原有的方法创建一个Fragment
fun createInstance(args: Bundle) 
              = MyFragment().apply { arguments = args }

我们也能够通过T.apply的链式调用创建一个Intent:

// 普通创建Intent方法
fun createIntent(intentData: String, intentAction: String): Intent {
    val intent = Intent()
    intent.action = intentAction
    intent.data=Uri.parse(intentData)
    return intent
}

// 通过apply函数的链式调用创建Intent
fun createIntent(intentData: String, intentAction: String) =
        Intent().apply { action = intentAction }
                .apply { data = Uri.parse(intentData) }

如果觉得我的文章能够帮助到你,也欢迎关注我的微信公众号「晨雨细曲」,有新技术和知识会推送在这上面。

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Kotlin协程的作用域函数有五种:let、run、with、apply以及also。这些函数基本上做了同样的事情:在一个对象上执行一个代码块。例如,我们可以使用apply函数来初始化一个对象的属性,如下所示:val adam = Person("Adam").apply { age = 20 city = ... }。通过apply函数,我们可以在代码块内直接访问和修改对象的属性,从而简化了对象的初始化过程。 在使用协程的过程中,如果我们想要判断协程是否被取消了,根据不同的情况采取相应的处理逻辑,可以使用if语句和isActive属性来判断。但是这样做可能比较繁琐且容易出错。另一种更简洁的方式是使用yield函数,yield函数是官方协程框架提供的一个对逻辑没有影响的挂起函数。通过使用yield函数,我们可以在协程中暂停执行,并且不需要关心协程的取消状态。 除了官方提供的几种作用域函数外,我们还可以通过继承或组合的方式创建自定义的协程作用域。例如,在ViewModel或Service中,我们可以使用SupervisorJob来创建自己的CoroutineScope对象。通过自定义的协程作用域,我们可以管理和控制协程的生命周期,并且可以处理协程中可能发生的异常。比如,在ViewModel中,我们可以在onCleared方法中取消自定义的协程作用域,确保协程的正确关闭。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Kotlin作用域函数之间的区别和使用场景详解](https://download.csdn.net/download/weixin_38690830/14014853)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [【深入理解Kotlin协程】协程作用域、启动模式、调度器、异常和取消【使用篇】](https://blog.csdn.net/lyabc123456/article/details/127800121)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值