我希望applyif
的工作方式如下:
builder.applyif(<condition expression>) {
builder.set...
}
与...相等:
builder.apply {
if (<condition expression>) {
builder.set...
}
}
这可能吗?
我希望applyif
的工作方式如下:
builder.applyif(<condition expression>) {
builder.set...
}
与...相等:
builder.apply {
if (<condition expression>) {
builder.set...
}
}
这可能吗?
当然可以。你几乎可以编写任何东西,但是不要重复造轮子。看一下答案底部的标准 Kotlin 方法,它可能已经满足您的需求(虽然并非完全等同于applyIf
)。
现在,让我们看一下如何实现一个applyIf
:
inline fun <T> T.applyIf(predicate: T.() -> Boolean, block: T.() -> Unit): T = apply {
if (predicate(this))
block(this)
}
如果使用lambda实现扩展函数,请不要忘记inline
关键字。
以下是上述内容的示例用法:
// sample class
class ADemo {
fun isTrue() = true
}
// sample usage using method references
ADemo().applyIf(ADemo::isTrue, ::println)
// or if you prefer or require it, here without
ADemo().applyIf( { isTrue() } ) {
println(this)
}
如果你只想提供一个布尔值,可以使用以下扩展函数:
inline fun <T> T.applyIf(condition : Boolean, block : T.() -> Unit) : T = apply {
if(condition) block(this)
}
然后使用以下方式调用:
val someCondition = true
ADemo().applyIf(someCondition) {
println(this)
}
现在有一个可能更多人熟悉的Kotlin标准方式:
ADemo().takeIf(ADemo::isTrue)
?.apply(::println)
// or
ADemo().takeIf { it.isTrue() }
?.apply { println(this) }
如果他们记得的话(我实际上是在看到Marko Topolnik的评论之前才想起来的),他们应该立即知道发生了什么。
但是,如果您在调用takeIf
后需要给定值(即ADemo()
),则此方法可能不适合您,因为以下内容将把变量设置为null
:
val x = ADemo().takeIf { false }
?.apply { println(this) /* never called */ }
// now x = null
而以下代码将会把变量设置为ADemo
实例:
val x = ADemo().applyIf(false) { println(this) /* also not called */ }
// now x contains the ADemo()-instance
链式调用构建器可能不是很好。但你也可以通过标准的Kotlin函数实现,将takeIf
与apply
或also
结合使用(或者使用with
、let
、run
,具体取决于你是否想要返回某些内容,以及是喜欢使用it
还是this
):
val x = builder.apply {
takeIf { false }
?.apply(::println) // not called
takeIf { true }
?.apply(::println) // called
}
// x contains the builder
applyIf
确实可以使代码变得更好:val x = builder.applyIf(false, ::println) // not called
.applyIf(true) {
println(this) // called
}
// x contains the builder
T
的情况下执行takeIf
?在上面的示例中,`ADemo().takeIf(ADemo::isTrue)`
ADemo将被创建,然后仅在谓词为真时才会被取走。是否有一种方法只在谓词为真时执行T
? - Nickif
或when
的工作.. - Roland当然可以,你只需要一个扩展函数,这样你就可以在builder
上调用它,并且你需要它接受一个Boolean
参数和要执行的lambda表达式。
如果你查看apply
函数本身的源代码,它将帮助你完成大部分实现:
public inline fun <T> T.apply(block: T.() -> Unit): T {
block()
return this
}
applyIf
可以非常简单地实现如下:inline fun <T> T.applyIf(condition: Boolean, block: T.() -> Unit): T {
return if (condition) this.apply(block) else this
}
使用方法如下:
builder.applyIf(x > 200) {
setSomething()
}
fun <T> T.applyIf(condition: Boolean, block: T.() -> T) = if (condition) block() else this
fun main() {
println("a".applyIf(true) { uppercase() }) // A
println("a".applyIf(false) { uppercase() }) // a
}
对于不可变的值(例如Compose Modifier
),apply
将无法工作,因此必须显式地返回this
,类似于这样
inline fun <T> T.applyIf(predicate: T.() -> Boolean, block: T.() -> T): T =
if (predicate())
block(this)
else
this
builder.takeIf { condition_code }?.apply { applied_code }
。任何新读者都会熟悉这些内容,不必学习和记忆你手写的扩展函数。 - Marko Topolnikbuilder.takeIf { condition_code }?.apply { applied_code }
如果条件为false则返回null,而所要求的applyif
返回一个构建器对象,无论条件是true还是false,在我看来这是在构建器上链接调用时所需要的。 - Michal Borowieckibuilder
变量中拥有了构建器对象,并且直接在闭合的}
上链接并不比引入自定义API更具有优势。如果你认为坚持使用标准库中熟悉的习语没有任何价值,那么你可能会有不同的看法。 - Marko Topolnik