我有几个这类代码的实例:

enum class Cat { Fat, Greedy, Sleepy, Lasagna }
...
when (cat) {
    Fat -> // do fat
    Greedy -> // do greedy
    else -> throw RuntimeException("You have a $cat cat - I don't know about those.")
}

我希望它说"你有一只$CAT-我只知道胖子和贪婪",但我不想在添加新的子句时更新消息.

  • 有没有办法读懂‘When’从句中已知的选项?(我怀疑没有,但Kotlin总是令人惊讶.)

我不想用需要与WHEN子句保持最新的标志来装饰枚举,也不想将lambdas添加到枚举中,从而将WHEN子句转换为基于调度的逻辑.

谢谢.

推荐答案

这样的东西只能用平等判断when.不清楚你想在这样的情况下发生什么:

when (foo) {
    is Int -> { ... }
    is String -> { ... }
    in someList -> { ... }
    else -> // what values would "the options known by a 'when'" be?
}

要对when进行实际操作,您需要某种批注处理来查看when表达式,并生成绑定值的列表.没有语言功能可以做到这一点.

我建议你只写你自己的when种.一个简单的实施是:

fun <T, R> `when`(target: T, vararg clauses: Pair<T, () -> R>, `else`: (Set<T>) -> R) {
    (clauses.firstOrNull { it == target }?.second?.invoke()) ?:
        `else`(clauses.map { it.first }.toSet())
}
`when` (cat,
    Fat to {},
    Greedy to {},
    `else` = { boundValues ->
        throw RuntimeException("You have a $cat cat - I only know about ${ boundValues.joinToString { it.toString() } }.")
    }
)

当然,您可以通过使用DSL builder模式使其看起来更类似于实际的when.例如,这里我实现了一个支持在一个子句中放置多个值的子句.

data class WhenClause<T, R>(val matchedValues: List<T>, val action: () -> R) {
    fun evaluate(value: T): R? =
        action.takeIf { value in matchedValues }?.invoke()
}

class WhenContext<T, R> {
    private val clauses = mutableListOf<WhenClause<T, R>>()

    private var `else`: ((Set<T>) -> R)? = null

    fun evaluate(value: T) =
        clauses.firstNotNullOfOrNull { it.evaluate(value) } ?:
            `else`?.invoke(clauses.flatMap { it.matchedValues }.toSet()) ?:
            error("Not exhaustive")

    infix fun T.then(block: () -> R) {
        clauses.add(WhenClause(listOf(this), block))
    }
    
    infix fun T.or(other: T) = listOf(this, other)
    infix fun List<T>.or(other: T) = plusElement(other)

    infix fun List<T>.then(block: () -> R) {
        clauses.add(WhenClause(this, block))
    }

    fun `else`(block: (Set<T>) -> R) {
        this.`else` = block
    }
}

inline fun <T, R> `when`(value: T, block: WhenContext<T, R>.() -> R): R {
    val context = WhenContext<T, R>()
    context.block()
    return context.evaluate(value)
}

用法示例:

`when` (cat) {
    (Fat or Greedy) then { }
    `else` { boundValues ->
        throw RuntimeException("You have a $cat cat - I only know about ${ boundValues.joinToString { it.toString() } }.")
    }
}

Kotlin相关问答推荐

映射中列表类型的Kotlin可空接收器?

为什么 Kotlin 中没有 init 块的注释

kotlin短路列表判断吗?

通过快捷键手动砍掉功能参数等

如何将光标从一个文本字段传递到 Jetpack Compose 中的其他文本字段?

根据字符串值动态过滤数组列表 - kotlin

如何处理基于枚举提前返回的 forEach 循环,Kotlin 中的一条路径除外

使用 Kotlin 协程时 Room dao 类出错

如何使用 Hilt 注入应用程序:ViewModel 中的上下文?

`this@classname` 在 Kotlin 中是什么意思?

Kotlin 枚举中的循环引用

Kotlin 语言是如何用 Kotlin 编写的?

Kotlin - 来自 KType 的 KClass<*>

如何捕获传递给模拟函数的参数并返回它?

在Kotlin中为Android编写库会有开销吗?

在 suspendCoroutine 块中调用挂起函数的适当方法是什么?

Kotlin中的属性(properties)和参数(parameters)有什么区别?

如何计算Kotlin中的百分比

Kotlin 对象 vs 伴生对象(companion-object) vs 包作用域(package scoped)方法

Kotlin反射不可用