除了作用域函数外,标准库还包含函数 takeIf
及 takeUnless
看看 takeIf 和 takeUnless 的实现:
// takeIf
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? {
contract {
callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
}
return if (predicate(this)) this else null
}
// takeUnless
public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? {
contract {
callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
}
return if (!predicate(this)) this else null
}
takeIf : 接收一个判断条件表达式,如果判断表达式为 true 则返回对象本身,false返回 null。
takeUnless: 与 takeIf 相反, 如果判断表达式为 true 则返回 null,false 返回对象本身。
该对象可以作为 lambda 参数 (it) 使用,过滤条件位于函数的 {} 中
fun main() {
val number = Random.nextInt(100)
val evenOrNull = number.takeIf { it % 2 == 0 }
val oddOrNull = number.takeUnless { it % 2 == 0 }
println("偶数: $evenOrNull, 奇数: $oddOrNull")
}
控制台输出:
偶数: 80, 奇数: null
当在 takeIf
及 takeUnless
之后链式调用其他函数,不要忘记执行空检查或安全调用(?.
),因为他们的返回值是可为空的。
fun main() {
val str = "Hello"
val caps = str.takeIf { it.isNotEmpty() }?.toUpperCase()
//val caps = str.takeIf { it.isNotEmpty() }.toUpperCase() // 编译错误
println(caps)
}
控制台输出:
HELLO
takeIf
及 takeUnless
与作用域函数一起特别有用。 一个很好的例子是用 let
链接它们,以便在与给定谓词匹配的对象上运行代码块。 为此,请在对象上调用 takeIf
,然后通过安全调用(?.
)调用 let
。对于与谓词不匹配的对象,takeIf
返回 null,并且不调用 let
。
fun main() {
fun displaySubstringPosition(input: String, sub: String) {
input.indexOf(sub).takeIf { it > 0 }?.let {
println("The substring $sub is found in $input.")
println("Its start position is $it.")
}
}
displaySubstringPosition("010000011", "11")
displaySubstringPosition("010000011", "12")
}
控制台输出:
The substring 11 is found in 010000011.
Its start position is 7.
没有标准库函数时,相同的函数看起来是这样的:
fun main() {
fun displaySubstringPosition(input: String, sub: String) {
val index = input.indexOf(sub)
if (index >= 0) {
println("The substring $sub is found in $input.")
println("Its start position is $index.")
}
}
displaySubstringPosition("010000011", "11")
displaySubstringPosition("010000011", "12")
}
使用场景
有这么一种场景可以使用,如请求后台返回了一个 UserCallback 对象,需要在界面显示这个 UserCallback 包含的一个 address 字符串,如果返回的 address 如果是 null 或空字符串则显示“未设置”。
以前这种场景一般都是这样实现的:
val addr = UserCallback.address
textView.text = if (addr.isNullOrBlank()) "未设置" else addr
但使用 takeIf
或 takeUnless
可以这样实现更为直接:
textView.text = UserCallback.address.takeUnless { it.isNullOrBlank() }?:"未设置"
// 或者使用takeIf的写法
// textView.text = UserCallback.address.takeIf { !it.isNullOrBlank() }?:"未设置"