如何为Kotlin实现applyif?

zaqlnxep  于 2023-05-18  发布在  Kotlin
关注(0)|答案(4)|浏览(129)

我想有一个applyif来工作:

builder.applyif(<condition expression>) {
    builder.set...
}

等于:

builder.apply {
    if (<condition expression>) {
        builder.set...
    }
}

这可能吗?

ca1c2owp

ca1c2owp1#

是的,当然。你几乎可以编程任何东西,但不要重新发明轮子。看看答案的底部,可以看到一个没有自己的扩展函数的标准Kotlin方法,它可能已经满足了你的需求(虽然不完全是applyIf)。
现在,让我们看看如何实现applyIf

inline fun <T> T.applyIf(predicate: T.() -> Boolean, block: T.() -> Unit): T = apply { 
  if (predicate(this)) 
    block(this) 
}

如果你用lambdas实现扩展函数,请不要忘记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 Topolniks的评论才记得),他们应该立即知道发生了什么。但是,如果您需要给定的值(即例如ADemo())在调用takeIf之后,这种方法可能不适用于您,因为下面将把变量设置为null

val x = ADemo().takeIf { false }
               ?.apply { println(this) /* never called */ }
// now x = null

而下面的代码会将变量设置为ADemo-instance:

val x = ADemo().applyIf(false) { println(this) /* also not called */ }
// now x contains the ADemo()-instance

那么链接构建器调用可能就不那么好了。你也可以通过标准的Kotlin函数来完成这个任务,方法是将takeIfapplyalso(或者withletrun,取决于你是否想要返回某些东西,或者你更喜欢使用itthis)组合在一起:

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
ki0zmccv

ki0zmccv2#

当然可以,你只需要一个扩展函数,这样你就可以在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()
}
dauxcl2d

dauxcl2d3#

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
}
owfi6suc

owfi6suc4#

一个不可变的值(e。例如,Compose Modifier)如果apply不起作用,则必须显式返回this,类似于以下内容

inline fun <T> T.applyIf(predicate: T.() -> Boolean, block: T.() -> T): T =
    if (predicate())
        block(this)
    else
        this

相关问题