如何为Kotlin实施applyif?

Eld*_*rry 5 kotlin

我想要像以下一样applyif工作:

builder.applyif(<condition expression>) {
    builder.set...
}
Run Code Online (Sandbox Code Playgroud)

等于:

builder.apply {
    if (<condition expression>) {
        builder.set...
    }
}
Run Code Online (Sandbox Code Playgroud)

那可能吗?

Rol*_*and 8

当然是.你几乎可以编程任何东西,不要重新发明轮子.查看答案的底部,看看没有自己的扩展功能的标准Kotlin方法,这可能已经满足您的需求(不完全如此applyIf).

但是,现在让我们看看如何applyIf实现:

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

inline如果您正在使用lambdas实现扩展功能,请不要忘记.

以下是上述示例用法.

// 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)
}
Run Code Online (Sandbox Code Playgroud)

如果您只想提供布尔值,可以使用以下扩展函数:

inline fun <T> T.applyIf(condition : Boolean, block : T.() -> Unit) : T = apply { 
  if(condition) block(this) 
}
Run Code Online (Sandbox Code Playgroud)

并称之为:

val someCondition = true
ADemo().applyIf(someCondition) {
  println(this)
}
Run Code Online (Sandbox Code Playgroud)

现在可以用更多人熟悉的Kotlin标准方式:

ADemo().takeIf(ADemo::isTrue)
       ?.apply(::println)

// or
ADemo().takeIf { it.isTrue() }
       ?.apply { println(this) }
Run Code Online (Sandbox Code Playgroud)

如果他们确实记得(我实际上没有看到Marko Topolniks评论),他们应该立即知道发生了什么.但是,如果您ADemo()在调用takeIf此方法后需要给定值(即),则可能对您不起作用,因为以下将变量设置为null:

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

而以下将宁愿将变量设置为ADemo-instance:

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

链接构建器调用可能不是那么好.你仍然可以通过标准科特林功能相结合,还做到这一点takeIfapplyalso(或者with,let,run,这取决于你是否想返回的东西或者不是,或者您喜欢与工作itthis):

val x = builder.apply {
  takeIf { false }
    ?.apply(::println) // not called
  takeIf { true }
    ?.apply(::println) // called
}
// x contains the builder
Run Code Online (Sandbox Code Playgroud)

但是,我们几乎就在那里,你已经在你的问题中.使用applyIf-usage 同样看起来更好看:

val x = builder.applyIf(false, ::println) // not called
               .applyIf(true) { 
                 println(this) // called
               }
// x contains the builder
Run Code Online (Sandbox Code Playgroud)