跳转至

使用Kotlin takeIf(或takeUnless)

在Kotlin的标准函数,有两大函数,即takeIftakeUnless,乍一看,有什么特别之处呢?这几乎就是if

或者极端点,把每一个if语句改成类似下面(不推荐)。

//原始代码
ifstatus{doThis()}
//修改后的代码
takeIf {status}apply {doThis()}

深入探讨

像其他任何东西一样,takeIf(或takeUnless)确实有它的使用场景。我通过不同情况分享我对他们的理解。在此之前,让我们看看它的实现。

函数签名

public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? 
    = if (predicate(this)) this else null

从函数签名,我们注意到

  1. 它是从T对象本身调用的。即T.takeIf
  2. predicate函数以T对象为参数
  3. 等待predicate评估后它返回thisnull

合理的使用情况

基于以上特点,我可以推导出它相对于if的使用情况,如下:

1.它是从T对象本身调用的。即T.takeIf

它可以很好处理可空性检查。一个例子如下

//原始代码
ifsomeObject= null && status{ 
   doThis()
}
//改进的代码
someObject.takeIf {status}apply {doThis()}

2.predicate函数以T对象为参数

由于将T作为predicate的参数,所以可以进一步简化takeIf代码

//原始代码
ifsomeObject= null && someObject.status{ 
   doThis()
} 
//更好的代码
ifsomeObject.status == true{ 
   doThis()
}
//改进的代码
someObject.takeIf {it.status} ?. apply {doThis()}

更好的代码的确还可以,但需要显式的true关键词,所以并不理想。

3.等待predicate评估后它返回thisnull

既然它返回this,那就可以用来进行链式调用。因此,下面代码可以优化

//原始代码
ifsomeObject= null && someObject.status{ 
   someObject.doThis()
}
//改进的代码
someObject.takeIf {status}doThis()

或者实现获取数据或退出的更好方式(例子从Kotlin Doc中摘取)

val index 
   = input.indexOfkeyword.takeIf {it> = 0}?:error(“Error”)
val outFile 
   = FileoutputDir.path.takeIf {it.exists()}?:return  false

注意

看看下面的代码。

//语法上仍然正确。但逻辑错误!
someObject.takeIf {status} .apply {doThis()}

//正确的(注意可空性检查?)
someObject.takeIf {status} .apply {doThis()}

doThis()在第一行中不管statustrue 还是 false 都会执行。因为 即使takeIf返回null,它仍然会被调用。(这里假设doThis()不是someObject的函数)

所以在这里,第二行的? 是非常微妙且重要的。