在Kotlin`when`语句(或其他分支构造)中,将函数或lambda作为条件包括在内的最简洁的方法是什么?

在Kotlin`when`语句(或其他分支构造)中,将函数或lambda作为条件包括在内的最简洁的方法是什么?,kotlin,lambda,functional-programming,conditional-statements,control-flow,Kotlin,Lambda,Functional Programming,Conditional Statements,Control Flow,我在处理字符串,我找到了答案:当语句中有一个覆盖equals的自定义类时,可以将正则表达式放在语句中。虽然这确实有效地使用了类型系统将语法糖塞进了when语句中,但我发现下面的语句非常难看,而且我永远不会在我打算与其他开发人员共享的代码中这样做(引用): 其中RegexWhenArgument的最低定义为: class RegexWhenArgument (val whenArgument: CharSequence) { operator fun equals(whenEntry: R

我在处理字符串,我找到了答案:当语句中有一个覆盖equals的自定义类时,可以将正则表达式放在
语句中。虽然这确实有效地使用了类型系统将语法糖塞进了
when
语句中,但我发现下面的语句非常难看,而且我永远不会在我打算与其他开发人员共享的代码中这样做(引用):

其中RegexWhenArgument的最低定义为:

class RegexWhenArgument (val whenArgument: CharSequence) {
    operator fun equals(whenEntry: Regex) = whenEntry.matches(whenArgument)
    override operator fun equals(whenEntry: Any?) = (whenArgument == whenEntry)
}
(完)

我认为在
时将arg传递给
,然后引用在arg类型上操作的函数,可读性会更好。举一个人为的例子:

// local declaration
val startsWithFn: (String) -> Boolean = {s -> s.startsWith("fn:")}

when(givenString) {
    ::startsWithHelp -> printHelp()
    startsWithFn -> println("Hello, ${givenString.substring(3)}!")
}

// package level function
fun startsWithHelp(s:String) = s.startsWith("help", true)

但当然,这段代码不会编译。有没有一种方法可以做到可读、可维护和简洁?也许使用流?有经验的Kotlin开发人员会怎么做?

您的问题有很多解决方案。我将从简单的开始,然后进入更复杂的部分


毫无争议 说:

when
也可用作
if
-
else if
链的替代品。如果没有 参数,则分支条件仅为布尔值 表达式,并在其条件为true时执行分支

用例:

when {
    startsWithHelp(givenString) -> printHelp()
    startsWithFn(givenString) -> println("Hello, ${givenString.substring(3)}!")
}
fun <T> whenArg(arg: T) = object {
    override fun equals(other: Any?) =
        if (other is Function1<*, *>) 
            (other as Function1<T, Boolean>)(arg)
        else arg == other
}
when (whenArg(givenString)) {
    ::startsWithHelp -> printHelp()
    startsWithFn -> println("Hello, ${givenString.substring(3)}!")
}
@DslMarker
annotation class WhichDsl

// This object is used for preventing client code from creating nested
// branches. You can omit it if you need them, but I highly recommend
// not to do this because nested branches may be confusing. 
@WhichDsl
object WhichCase

// R type parameter represents a type of expression result
@WhichDsl
class Which<T, R>(val arg: T) {
    // R? is not used here because R can be nullable itself 
    var result: Holder<R>? = null

    inline operator fun ((T) -> Boolean).invoke(code: WhichCase.() -> R) {
        if (result == null && invoke(arg)) result = Holder(code(WhichCase))
    }

    // else analog
    inline fun other(code: WhichCase.() -> R) = result?.element ?: code(WhichCase)
}

data class Holder<out T>(val element: T)

inline fun <T, R> which(arg: T, @BuilderInference code: Which<T, R>.() -> R) =
    Which<T, R>(arg).code()
which(givenString) {
    ::startsWithHelp { printHelp() }
    startsWithFn { println("Hello, ${givenString.substring(3)}!") }
}
val int = which(givenString) {
    ::startsWithHelp { 0 }
    startsWithFn { 1 }
    other { error("Unknown command: $givenString") }
}
inline fun <R> Which<*, R>.orThrow(message: () -> String) =
    other { throw NoWhenBranchMatchedException(message()) }

val <R> Which<*, R>.orThrow get() = orThrow { "No branch matches to $arg" }

inline fun <T, R> Which<T, R>.branch(condition: (T) -> Boolean, code: WhichCase.() -> R) =
    condition(code)

inline fun <T, R> Which<T, R>.case(value: T, code: WhichCase.() -> R) =
    branch({ it == value }, code)

fun <T : CharSequence, R> Which<T, R>.regex(regex: Regex, code: WhichCase.() -> R) =
    branch({ regex.matches(it) }, code)

fun <T : Comparable<T>, R> Which<T, R>.range(range: ClosedRange<T>, code: WhichCase.() -> R) =
    branch({ it in range }, code)

inline fun <T> Which<T, *>.sideBranch(condition: (T) -> Boolean, code: WhichCase.() -> Unit) {
    if (condition(arg)) code(WhichCase)
}

fun <T> Which<T, *>.sideCase(value: T, code: WhichCase.() -> Unit) =
    sideBranch({ it == value }, code)

inline fun <R> Which<*, R>.dropResult(condition: WhichCase.(R) -> Boolean = { _ -> true }) {
    result?.let { (element) ->
        if (WhichCase.condition(element)) result = null
    }
}

inline fun <T, R> Which<T, R>.subWhich(condition: (T) -> Boolean, code: Which<T, R>.() -> R) =
    branch(condition) {
    which(this@subBranch.arg, code)
}
专业人士

  • 不需要任何附加代码

  • 易懂

  • 缺点

  • 样板文件
    (givenString)

  • 重写等于的参数包装器 所需的包装器类似于
    RegexWhenArgument
    ,但它不是选中
    Regex
    ,而是调用
    (字符串)->Boolean
    String
    没有什么特别之处,所以我将使用

    包装函数:

    when {
        startsWithHelp(givenString) -> printHelp()
        startsWithFn(givenString) -> println("Hello, ${givenString.substring(3)}!")
    }
    
    fun <T> whenArg(arg: T) = object {
        override fun equals(other: Any?) =
            if (other is Function1<*, *>) 
                (other as Function1<T, Boolean>)(arg)
            else arg == other
    }
    
    when (whenArg(givenString)) {
        ::startsWithHelp -> printHelp()
        startsWithFn -> println("Hello, ${givenString.substring(3)}!")
    }
    
    @DslMarker
    annotation class WhichDsl
    
    // This object is used for preventing client code from creating nested
    // branches. You can omit it if you need them, but I highly recommend
    // not to do this because nested branches may be confusing. 
    @WhichDsl
    object WhichCase
    
    // R type parameter represents a type of expression result
    @WhichDsl
    class Which<T, R>(val arg: T) {
        // R? is not used here because R can be nullable itself 
        var result: Holder<R>? = null
    
        inline operator fun ((T) -> Boolean).invoke(code: WhichCase.() -> R) {
            if (result == null && invoke(arg)) result = Holder(code(WhichCase))
        }
    
        // else analog
        inline fun other(code: WhichCase.() -> R) = result?.element ?: code(WhichCase)
    }
    
    data class Holder<out T>(val element: T)
    
    inline fun <T, R> which(arg: T, @BuilderInference code: Which<T, R>.() -> R) =
        Which<T, R>(arg).code()
    
    which(givenString) {
        ::startsWithHelp { printHelp() }
        startsWithFn { println("Hello, ${givenString.substring(3)}!") }
    }
    
    val int = which(givenString) {
        ::startsWithHelp { 0 }
        startsWithFn { 1 }
        other { error("Unknown command: $givenString") }
    }
    
    inline fun <R> Which<*, R>.orThrow(message: () -> String) =
        other { throw NoWhenBranchMatchedException(message()) }
    
    val <R> Which<*, R>.orThrow get() = orThrow { "No branch matches to $arg" }
    
    inline fun <T, R> Which<T, R>.branch(condition: (T) -> Boolean, code: WhichCase.() -> R) =
        condition(code)
    
    inline fun <T, R> Which<T, R>.case(value: T, code: WhichCase.() -> R) =
        branch({ it == value }, code)
    
    fun <T : CharSequence, R> Which<T, R>.regex(regex: Regex, code: WhichCase.() -> R) =
        branch({ regex.matches(it) }, code)
    
    fun <T : Comparable<T>, R> Which<T, R>.range(range: ClosedRange<T>, code: WhichCase.() -> R) =
        branch({ it in range }, code)
    
    inline fun <T> Which<T, *>.sideBranch(condition: (T) -> Boolean, code: WhichCase.() -> Unit) {
        if (condition(arg)) code(WhichCase)
    }
    
    fun <T> Which<T, *>.sideCase(value: T, code: WhichCase.() -> Unit) =
        sideBranch({ it == value }, code)
    
    inline fun <R> Which<*, R>.dropResult(condition: WhichCase.(R) -> Boolean = { _ -> true }) {
        result?.let { (element) ->
            if (WhichCase.condition(element)) result = null
        }
    }
    
    inline fun <T, R> Which<T, R>.subWhich(condition: (T) -> Boolean, code: Which<T, R>.() -> R) =
        branch(condition) {
        which(this@subBranch.arg, code)
    }
    
    优点:

    when {
        startsWithHelp(givenString) -> printHelp()
        startsWithFn(givenString) -> println("Hello, ${givenString.substring(3)}!")
    }
    
    fun <T> whenArg(arg: T) = object {
        override fun equals(other: Any?) =
            if (other is Function1<*, *>) 
                (other as Function1<T, Boolean>)(arg)
            else arg == other
    }
    
    when (whenArg(givenString)) {
        ::startsWithHelp -> printHelp()
        startsWithFn -> println("Hello, ${givenString.substring(3)}!")
    }
    
    @DslMarker
    annotation class WhichDsl
    
    // This object is used for preventing client code from creating nested
    // branches. You can omit it if you need them, but I highly recommend
    // not to do this because nested branches may be confusing. 
    @WhichDsl
    object WhichCase
    
    // R type parameter represents a type of expression result
    @WhichDsl
    class Which<T, R>(val arg: T) {
        // R? is not used here because R can be nullable itself 
        var result: Holder<R>? = null
    
        inline operator fun ((T) -> Boolean).invoke(code: WhichCase.() -> R) {
            if (result == null && invoke(arg)) result = Holder(code(WhichCase))
        }
    
        // else analog
        inline fun other(code: WhichCase.() -> R) = result?.element ?: code(WhichCase)
    }
    
    data class Holder<out T>(val element: T)
    
    inline fun <T, R> which(arg: T, @BuilderInference code: Which<T, R>.() -> R) =
        Which<T, R>(arg).code()
    
    which(givenString) {
        ::startsWithHelp { printHelp() }
        startsWithFn { println("Hello, ${givenString.substring(3)}!") }
    }
    
    val int = which(givenString) {
        ::startsWithHelp { 0 }
        startsWithFn { 1 }
        other { error("Unknown command: $givenString") }
    }
    
    inline fun <R> Which<*, R>.orThrow(message: () -> String) =
        other { throw NoWhenBranchMatchedException(message()) }
    
    val <R> Which<*, R>.orThrow get() = orThrow { "No branch matches to $arg" }
    
    inline fun <T, R> Which<T, R>.branch(condition: (T) -> Boolean, code: WhichCase.() -> R) =
        condition(code)
    
    inline fun <T, R> Which<T, R>.case(value: T, code: WhichCase.() -> R) =
        branch({ it == value }, code)
    
    fun <T : CharSequence, R> Which<T, R>.regex(regex: Regex, code: WhichCase.() -> R) =
        branch({ regex.matches(it) }, code)
    
    fun <T : Comparable<T>, R> Which<T, R>.range(range: ClosedRange<T>, code: WhichCase.() -> R) =
        branch({ it in range }, code)
    
    inline fun <T> Which<T, *>.sideBranch(condition: (T) -> Boolean, code: WhichCase.() -> Unit) {
        if (condition(arg)) code(WhichCase)
    }
    
    fun <T> Which<T, *>.sideCase(value: T, code: WhichCase.() -> Unit) =
        sideBranch({ it == value }, code)
    
    inline fun <R> Which<*, R>.dropResult(condition: WhichCase.(R) -> Boolean = { _ -> true }) {
        result?.let { (element) ->
            if (WhichCase.condition(element)) result = null
        }
    }
    
    inline fun <T, R> Which<T, R>.subWhich(condition: (T) -> Boolean, code: Which<T, R>.() -> R) =
        branch(condition) {
        which(this@subBranch.arg, code)
    }
    
  • 分支时,可以在
    中使用函数
    
  • 很容易理解
  • 只需要一个简单的包装函数
  • 缺点:

    when {
        startsWithHelp(givenString) -> printHelp()
        startsWithFn(givenString) -> println("Hello, ${givenString.substring(3)}!")
    }
    
    fun <T> whenArg(arg: T) = object {
        override fun equals(other: Any?) =
            if (other is Function1<*, *>) 
                (other as Function1<T, Boolean>)(arg)
            else arg == other
    }
    
    when (whenArg(givenString)) {
        ::startsWithHelp -> printHelp()
        startsWithFn -> println("Hello, ${givenString.substring(3)}!")
    }
    
    @DslMarker
    annotation class WhichDsl
    
    // This object is used for preventing client code from creating nested
    // branches. You can omit it if you need them, but I highly recommend
    // not to do this because nested branches may be confusing. 
    @WhichDsl
    object WhichCase
    
    // R type parameter represents a type of expression result
    @WhichDsl
    class Which<T, R>(val arg: T) {
        // R? is not used here because R can be nullable itself 
        var result: Holder<R>? = null
    
        inline operator fun ((T) -> Boolean).invoke(code: WhichCase.() -> R) {
            if (result == null && invoke(arg)) result = Holder(code(WhichCase))
        }
    
        // else analog
        inline fun other(code: WhichCase.() -> R) = result?.element ?: code(WhichCase)
    }
    
    data class Holder<out T>(val element: T)
    
    inline fun <T, R> which(arg: T, @BuilderInference code: Which<T, R>.() -> R) =
        Which<T, R>(arg).code()
    
    which(givenString) {
        ::startsWithHelp { printHelp() }
        startsWithFn { println("Hello, ${givenString.substring(3)}!") }
    }
    
    val int = which(givenString) {
        ::startsWithHelp { 0 }
        startsWithFn { 1 }
        other { error("Unknown command: $givenString") }
    }
    
    inline fun <R> Which<*, R>.orThrow(message: () -> String) =
        other { throw NoWhenBranchMatchedException(message()) }
    
    val <R> Which<*, R>.orThrow get() = orThrow { "No branch matches to $arg" }
    
    inline fun <T, R> Which<T, R>.branch(condition: (T) -> Boolean, code: WhichCase.() -> R) =
        condition(code)
    
    inline fun <T, R> Which<T, R>.case(value: T, code: WhichCase.() -> R) =
        branch({ it == value }, code)
    
    fun <T : CharSequence, R> Which<T, R>.regex(regex: Regex, code: WhichCase.() -> R) =
        branch({ regex.matches(it) }, code)
    
    fun <T : Comparable<T>, R> Which<T, R>.range(range: ClosedRange<T>, code: WhichCase.() -> R) =
        branch({ it in range }, code)
    
    inline fun <T> Which<T, *>.sideBranch(condition: (T) -> Boolean, code: WhichCase.() -> Unit) {
        if (condition(arg)) code(WhichCase)
    }
    
    fun <T> Which<T, *>.sideCase(value: T, code: WhichCase.() -> Unit) =
        sideBranch({ it == value }, code)
    
    inline fun <R> Which<*, R>.dropResult(condition: WhichCase.(R) -> Boolean = { _ -> true }) {
        result?.let { (element) ->
            if (WhichCase.condition(element)) result = null
        }
    }
    
    inline fun <T, R> Which<T, R>.subWhich(condition: (T) -> Boolean, code: Which<T, R>.() -> R) =
        branch(condition) {
        which(this@subBranch.arg, code)
    }
    
  • 很容易忘记包装一个论点
  • 在分支条件下,可能会意外使用错误类型的函数
  • 注意:此解决方案和下一个解决方案允许使用函数组合,可通过以下扩展创建:

    infix fun <T> ((T) -> Boolean).and(other: ((T) -> Boolean)) = { it: T -> invoke(it) && other(it) }
    infix fun <T> ((T) -> Boolean).or(other: ((T) -> Boolean)) = { it: T -> invoke(it) || other(it) }
    operator fun <T> ((T) -> Boolean).not() = { it: T -> !invoke(it) }
    
    语句用例:

    when {
        startsWithHelp(givenString) -> printHelp()
        startsWithFn(givenString) -> println("Hello, ${givenString.substring(3)}!")
    }
    
    fun <T> whenArg(arg: T) = object {
        override fun equals(other: Any?) =
            if (other is Function1<*, *>) 
                (other as Function1<T, Boolean>)(arg)
            else arg == other
    }
    
    when (whenArg(givenString)) {
        ::startsWithHelp -> printHelp()
        startsWithFn -> println("Hello, ${givenString.substring(3)}!")
    }
    
    @DslMarker
    annotation class WhichDsl
    
    // This object is used for preventing client code from creating nested
    // branches. You can omit it if you need them, but I highly recommend
    // not to do this because nested branches may be confusing. 
    @WhichDsl
    object WhichCase
    
    // R type parameter represents a type of expression result
    @WhichDsl
    class Which<T, R>(val arg: T) {
        // R? is not used here because R can be nullable itself 
        var result: Holder<R>? = null
    
        inline operator fun ((T) -> Boolean).invoke(code: WhichCase.() -> R) {
            if (result == null && invoke(arg)) result = Holder(code(WhichCase))
        }
    
        // else analog
        inline fun other(code: WhichCase.() -> R) = result?.element ?: code(WhichCase)
    }
    
    data class Holder<out T>(val element: T)
    
    inline fun <T, R> which(arg: T, @BuilderInference code: Which<T, R>.() -> R) =
        Which<T, R>(arg).code()
    
    which(givenString) {
        ::startsWithHelp { printHelp() }
        startsWithFn { println("Hello, ${givenString.substring(3)}!") }
    }
    
    val int = which(givenString) {
        ::startsWithHelp { 0 }
        startsWithFn { 1 }
        other { error("Unknown command: $givenString") }
    }
    
    inline fun <R> Which<*, R>.orThrow(message: () -> String) =
        other { throw NoWhenBranchMatchedException(message()) }
    
    val <R> Which<*, R>.orThrow get() = orThrow { "No branch matches to $arg" }
    
    inline fun <T, R> Which<T, R>.branch(condition: (T) -> Boolean, code: WhichCase.() -> R) =
        condition(code)
    
    inline fun <T, R> Which<T, R>.case(value: T, code: WhichCase.() -> R) =
        branch({ it == value }, code)
    
    fun <T : CharSequence, R> Which<T, R>.regex(regex: Regex, code: WhichCase.() -> R) =
        branch({ regex.matches(it) }, code)
    
    fun <T : Comparable<T>, R> Which<T, R>.range(range: ClosedRange<T>, code: WhichCase.() -> R) =
        branch({ it in range }, code)
    
    inline fun <T> Which<T, *>.sideBranch(condition: (T) -> Boolean, code: WhichCase.() -> Unit) {
        if (condition(arg)) code(WhichCase)
    }
    
    fun <T> Which<T, *>.sideCase(value: T, code: WhichCase.() -> Unit) =
        sideBranch({ it == value }, code)
    
    inline fun <R> Which<*, R>.dropResult(condition: WhichCase.(R) -> Boolean = { _ -> true }) {
        result?.let { (element) ->
            if (WhichCase.condition(element)) result = null
        }
    }
    
    inline fun <T, R> Which<T, R>.subWhich(condition: (T) -> Boolean, code: Which<T, R>.() -> R) =
        branch(condition) {
        which(this@subBranch.arg, code)
    }
    
    表达式用例:

    when {
        startsWithHelp(givenString) -> printHelp()
        startsWithFn(givenString) -> println("Hello, ${givenString.substring(3)}!")
    }
    
    fun <T> whenArg(arg: T) = object {
        override fun equals(other: Any?) =
            if (other is Function1<*, *>) 
                (other as Function1<T, Boolean>)(arg)
            else arg == other
    }
    
    when (whenArg(givenString)) {
        ::startsWithHelp -> printHelp()
        startsWithFn -> println("Hello, ${givenString.substring(3)}!")
    }
    
    @DslMarker
    annotation class WhichDsl
    
    // This object is used for preventing client code from creating nested
    // branches. You can omit it if you need them, but I highly recommend
    // not to do this because nested branches may be confusing. 
    @WhichDsl
    object WhichCase
    
    // R type parameter represents a type of expression result
    @WhichDsl
    class Which<T, R>(val arg: T) {
        // R? is not used here because R can be nullable itself 
        var result: Holder<R>? = null
    
        inline operator fun ((T) -> Boolean).invoke(code: WhichCase.() -> R) {
            if (result == null && invoke(arg)) result = Holder(code(WhichCase))
        }
    
        // else analog
        inline fun other(code: WhichCase.() -> R) = result?.element ?: code(WhichCase)
    }
    
    data class Holder<out T>(val element: T)
    
    inline fun <T, R> which(arg: T, @BuilderInference code: Which<T, R>.() -> R) =
        Which<T, R>(arg).code()
    
    which(givenString) {
        ::startsWithHelp { printHelp() }
        startsWithFn { println("Hello, ${givenString.substring(3)}!") }
    }
    
    val int = which(givenString) {
        ::startsWithHelp { 0 }
        startsWithFn { 1 }
        other { error("Unknown command: $givenString") }
    }
    
    inline fun <R> Which<*, R>.orThrow(message: () -> String) =
        other { throw NoWhenBranchMatchedException(message()) }
    
    val <R> Which<*, R>.orThrow get() = orThrow { "No branch matches to $arg" }
    
    inline fun <T, R> Which<T, R>.branch(condition: (T) -> Boolean, code: WhichCase.() -> R) =
        condition(code)
    
    inline fun <T, R> Which<T, R>.case(value: T, code: WhichCase.() -> R) =
        branch({ it == value }, code)
    
    fun <T : CharSequence, R> Which<T, R>.regex(regex: Regex, code: WhichCase.() -> R) =
        branch({ regex.matches(it) }, code)
    
    fun <T : Comparable<T>, R> Which<T, R>.range(range: ClosedRange<T>, code: WhichCase.() -> R) =
        branch({ it in range }, code)
    
    inline fun <T> Which<T, *>.sideBranch(condition: (T) -> Boolean, code: WhichCase.() -> Unit) {
        if (condition(arg)) code(WhichCase)
    }
    
    fun <T> Which<T, *>.sideCase(value: T, code: WhichCase.() -> Unit) =
        sideBranch({ it == value }, code)
    
    inline fun <R> Which<*, R>.dropResult(condition: WhichCase.(R) -> Boolean = { _ -> true }) {
        result?.let { (element) ->
            if (WhichCase.condition(element)) result = null
        }
    }
    
    inline fun <T, R> Which<T, R>.subWhich(condition: (T) -> Boolean, code: Which<T, R>.() -> R) =
        branch(condition) {
        which(this@subBranch.arg, code)
    }
    
    优点:

    when {
        startsWithHelp(givenString) -> printHelp()
        startsWithFn(givenString) -> println("Hello, ${givenString.substring(3)}!")
    }
    
    fun <T> whenArg(arg: T) = object {
        override fun equals(other: Any?) =
            if (other is Function1<*, *>) 
                (other as Function1<T, Boolean>)(arg)
            else arg == other
    }
    
    when (whenArg(givenString)) {
        ::startsWithHelp -> printHelp()
        startsWithFn -> println("Hello, ${givenString.substring(3)}!")
    }
    
    @DslMarker
    annotation class WhichDsl
    
    // This object is used for preventing client code from creating nested
    // branches. You can omit it if you need them, but I highly recommend
    // not to do this because nested branches may be confusing. 
    @WhichDsl
    object WhichCase
    
    // R type parameter represents a type of expression result
    @WhichDsl
    class Which<T, R>(val arg: T) {
        // R? is not used here because R can be nullable itself 
        var result: Holder<R>? = null
    
        inline operator fun ((T) -> Boolean).invoke(code: WhichCase.() -> R) {
            if (result == null && invoke(arg)) result = Holder(code(WhichCase))
        }
    
        // else analog
        inline fun other(code: WhichCase.() -> R) = result?.element ?: code(WhichCase)
    }
    
    data class Holder<out T>(val element: T)
    
    inline fun <T, R> which(arg: T, @BuilderInference code: Which<T, R>.() -> R) =
        Which<T, R>(arg).code()
    
    which(givenString) {
        ::startsWithHelp { printHelp() }
        startsWithFn { println("Hello, ${givenString.substring(3)}!") }
    }
    
    val int = which(givenString) {
        ::startsWithHelp { 0 }
        startsWithFn { 1 }
        other { error("Unknown command: $givenString") }
    }
    
    inline fun <R> Which<*, R>.orThrow(message: () -> String) =
        other { throw NoWhenBranchMatchedException(message()) }
    
    val <R> Which<*, R>.orThrow get() = orThrow { "No branch matches to $arg" }
    
    inline fun <T, R> Which<T, R>.branch(condition: (T) -> Boolean, code: WhichCase.() -> R) =
        condition(code)
    
    inline fun <T, R> Which<T, R>.case(value: T, code: WhichCase.() -> R) =
        branch({ it == value }, code)
    
    fun <T : CharSequence, R> Which<T, R>.regex(regex: Regex, code: WhichCase.() -> R) =
        branch({ regex.matches(it) }, code)
    
    fun <T : Comparable<T>, R> Which<T, R>.range(range: ClosedRange<T>, code: WhichCase.() -> R) =
        branch({ it in range }, code)
    
    inline fun <T> Which<T, *>.sideBranch(condition: (T) -> Boolean, code: WhichCase.() -> Unit) {
        if (condition(arg)) code(WhichCase)
    }
    
    fun <T> Which<T, *>.sideCase(value: T, code: WhichCase.() -> Unit) =
        sideBranch({ it == value }, code)
    
    inline fun <R> Which<*, R>.dropResult(condition: WhichCase.(R) -> Boolean = { _ -> true }) {
        result?.let { (element) ->
            if (WhichCase.condition(element)) result = null
        }
    }
    
    inline fun <T, R> Which<T, R>.subWhich(condition: (T) -> Boolean, code: Which<T, R>.() -> R) =
        branch(condition) {
        which(this@subBranch.arg, code)
    }
    
  • 可自定义语法
  • 可以添加新功能
  • 可以添加仅适用于某些参数类型的函数
  • 可以启用嵌套分支
  • 没有样板
  • 在编译时检查函数类型
  • 易读
  • 缺点:

    when {
        startsWithHelp(givenString) -> printHelp()
        startsWithFn(givenString) -> println("Hello, ${givenString.substring(3)}!")
    }
    
    fun <T> whenArg(arg: T) = object {
        override fun equals(other: Any?) =
            if (other is Function1<*, *>) 
                (other as Function1<T, Boolean>)(arg)
            else arg == other
    }
    
    when (whenArg(givenString)) {
        ::startsWithHelp -> printHelp()
        startsWithFn -> println("Hello, ${givenString.substring(3)}!")
    }
    
    @DslMarker
    annotation class WhichDsl
    
    // This object is used for preventing client code from creating nested
    // branches. You can omit it if you need them, but I highly recommend
    // not to do this because nested branches may be confusing. 
    @WhichDsl
    object WhichCase
    
    // R type parameter represents a type of expression result
    @WhichDsl
    class Which<T, R>(val arg: T) {
        // R? is not used here because R can be nullable itself 
        var result: Holder<R>? = null
    
        inline operator fun ((T) -> Boolean).invoke(code: WhichCase.() -> R) {
            if (result == null && invoke(arg)) result = Holder(code(WhichCase))
        }
    
        // else analog
        inline fun other(code: WhichCase.() -> R) = result?.element ?: code(WhichCase)
    }
    
    data class Holder<out T>(val element: T)
    
    inline fun <T, R> which(arg: T, @BuilderInference code: Which<T, R>.() -> R) =
        Which<T, R>(arg).code()
    
    which(givenString) {
        ::startsWithHelp { printHelp() }
        startsWithFn { println("Hello, ${givenString.substring(3)}!") }
    }
    
    val int = which(givenString) {
        ::startsWithHelp { 0 }
        startsWithFn { 1 }
        other { error("Unknown command: $givenString") }
    }
    
    inline fun <R> Which<*, R>.orThrow(message: () -> String) =
        other { throw NoWhenBranchMatchedException(message()) }
    
    val <R> Which<*, R>.orThrow get() = orThrow { "No branch matches to $arg" }
    
    inline fun <T, R> Which<T, R>.branch(condition: (T) -> Boolean, code: WhichCase.() -> R) =
        condition(code)
    
    inline fun <T, R> Which<T, R>.case(value: T, code: WhichCase.() -> R) =
        branch({ it == value }, code)
    
    fun <T : CharSequence, R> Which<T, R>.regex(regex: Regex, code: WhichCase.() -> R) =
        branch({ regex.matches(it) }, code)
    
    fun <T : Comparable<T>, R> Which<T, R>.range(range: ClosedRange<T>, code: WhichCase.() -> R) =
        branch({ it in range }, code)
    
    inline fun <T> Which<T, *>.sideBranch(condition: (T) -> Boolean, code: WhichCase.() -> Unit) {
        if (condition(arg)) code(WhichCase)
    }
    
    fun <T> Which<T, *>.sideCase(value: T, code: WhichCase.() -> Unit) =
        sideBranch({ it == value }, code)
    
    inline fun <R> Which<*, R>.dropResult(condition: WhichCase.(R) -> Boolean = { _ -> true }) {
        result?.let { (element) ->
            if (WhichCase.condition(element)) result = null
        }
    }
    
    inline fun <T, R> Which<T, R>.subWhich(condition: (T) -> Boolean, code: Which<T, R>.() -> R) =
        branch(condition) {
        which(this@subBranch.arg, code)
    }
    
  • 需要几个助手类和函数
  • 实验性
    @builderReference
    的使用(可以用表达式的显式类型声明和语句的单独方法替换)
  • 新语法学习可能需要一些时间
  • 扩展示例:

    when {
        startsWithHelp(givenString) -> printHelp()
        startsWithFn(givenString) -> println("Hello, ${givenString.substring(3)}!")
    }
    
    fun <T> whenArg(arg: T) = object {
        override fun equals(other: Any?) =
            if (other is Function1<*, *>) 
                (other as Function1<T, Boolean>)(arg)
            else arg == other
    }
    
    when (whenArg(givenString)) {
        ::startsWithHelp -> printHelp()
        startsWithFn -> println("Hello, ${givenString.substring(3)}!")
    }
    
    @DslMarker
    annotation class WhichDsl
    
    // This object is used for preventing client code from creating nested
    // branches. You can omit it if you need them, but I highly recommend
    // not to do this because nested branches may be confusing. 
    @WhichDsl
    object WhichCase
    
    // R type parameter represents a type of expression result
    @WhichDsl
    class Which<T, R>(val arg: T) {
        // R? is not used here because R can be nullable itself 
        var result: Holder<R>? = null
    
        inline operator fun ((T) -> Boolean).invoke(code: WhichCase.() -> R) {
            if (result == null && invoke(arg)) result = Holder(code(WhichCase))
        }
    
        // else analog
        inline fun other(code: WhichCase.() -> R) = result?.element ?: code(WhichCase)
    }
    
    data class Holder<out T>(val element: T)
    
    inline fun <T, R> which(arg: T, @BuilderInference code: Which<T, R>.() -> R) =
        Which<T, R>(arg).code()
    
    which(givenString) {
        ::startsWithHelp { printHelp() }
        startsWithFn { println("Hello, ${givenString.substring(3)}!") }
    }
    
    val int = which(givenString) {
        ::startsWithHelp { 0 }
        startsWithFn { 1 }
        other { error("Unknown command: $givenString") }
    }
    
    inline fun <R> Which<*, R>.orThrow(message: () -> String) =
        other { throw NoWhenBranchMatchedException(message()) }
    
    val <R> Which<*, R>.orThrow get() = orThrow { "No branch matches to $arg" }
    
    inline fun <T, R> Which<T, R>.branch(condition: (T) -> Boolean, code: WhichCase.() -> R) =
        condition(code)
    
    inline fun <T, R> Which<T, R>.case(value: T, code: WhichCase.() -> R) =
        branch({ it == value }, code)
    
    fun <T : CharSequence, R> Which<T, R>.regex(regex: Regex, code: WhichCase.() -> R) =
        branch({ regex.matches(it) }, code)
    
    fun <T : Comparable<T>, R> Which<T, R>.range(range: ClosedRange<T>, code: WhichCase.() -> R) =
        branch({ it in range }, code)
    
    inline fun <T> Which<T, *>.sideBranch(condition: (T) -> Boolean, code: WhichCase.() -> Unit) {
        if (condition(arg)) code(WhichCase)
    }
    
    fun <T> Which<T, *>.sideCase(value: T, code: WhichCase.() -> Unit) =
        sideBranch({ it == value }, code)
    
    inline fun <R> Which<*, R>.dropResult(condition: WhichCase.(R) -> Boolean = { _ -> true }) {
        result?.let { (element) ->
            if (WhichCase.condition(element)) result = null
        }
    }
    
    inline fun <T, R> Which<T, R>.subWhich(condition: (T) -> Boolean, code: Which<T, R>.() -> R) =
        branch(condition) {
        which(this@subBranch.arg, code)
    }
    
    inline.orThrow(消息:()->字符串)=
    其他{抛出NoWhenBranchMatchedException(message())}
    val Which.orThrow get()=orThrow{“没有与$arg匹配的分支”}
    inline fun Which.branch(条件:(T)->布尔,代码:WhichCase.(->R)=
    条件(代码)
    inline fun Which.case(值:T,代码:WhichCase.(->R)=
    分支({it==value},代码)
    regex(regex:regex,代码:WhichCase.(->R)=
    分支({regex.matches(it)},代码)
    有趣的。范围(范围:ClosedRange,代码:WhichCase.(->R)=
    分支({it in range},代码)
    inline fun Which.sideBranch(条件:(T)->布尔,代码:WhichCase.(->单位){
    if(条件(参数))代码(哪种情况)
    }
    有趣的Which.sideCase(值:T,代码:WhichCase.(->单位)=
    边分支({it==value},代码)
    dropResult(条件:WhichCase.(R)->Boolean={{uu->true}){
    结果?让{(元素)->
    if(WhichCase.condition(element))结果=null
    }
    }
    内联fun Which.subWhich(条件:(T)->布尔,代码:Which.(->R)=
    分支(条件){
    哪个(this@subBranch.arg,代码)
    }
    
    我真的,真的会做一连串的假设。我真的认为它看起来和读起来都很好。