对<,< =,> =,>使用Kotlin WHEN子句对比

时间:2017-07-28 15:34:46

标签: kotlin

我正在尝试将WHEN子句用于><比较。

这不编译。有没有办法在比较中使用普通的布尔运算符集(&lt;&lt;&lt; =,&gt; =&gt;)来启用它?

val foo = 2

// doesn't compile
when (foo) {
    > 0 -> doSomethingWhenPositive()
    0   -> doSomethingWhenZero()
    < 0 -> doSomethingWhenNegative()
}

我试图找到一个无限范围比较,但也无法使这项工作?是否可以将其写为无界范围?

// trying to get an unbounded range - doesn't compile
when (foo) {
    in 1.. -> doSomethingWhenPositive()
    else -> doSomethingElse()
}

你可以将整个表达式放在第二部分,这是好的,但似乎是不必要的重复。至少它编译和工作。

when {
    foo > 0 -> doSomethingWhenPositive()
    foo < 0 -> doSomethingWhenNegative()
    else -> doSomethingWhenZero()
}

但我不确定这比我们多年来一直在做的if-else替代方案更简单。类似的东西:

if ( foo > 0 ) {
    doSomethingWhenPositive()
}
else if (foo < 0) {
    doSomethingWhenNegative()
}
else {
    doSomethingWhenZero()
}

当然,现实问题比上面的问题更复杂,WHEN条款很有吸引力,但不能像我期望的那样进行比较。

5 个答案:

答案 0 :(得分:9)

即使像Kotlin这样灵活的语言也没有针对每个案例的“优雅”/ DRY解决方案。

你可以这样写:

when (foo) {
    in 0 .. Int.MAX_VALUE -> doSomethingWhenPositive()
    0    -> doSomethingWhenZero()
    else -> doSomethingWhenNegative()
}

但是你依赖变量类型。

我认为以下形式是Kotlin中最惯用的形式:

when {
    foo > 0  -> doSomethingWhenPositive()
    foo == 0 -> doSomethingWhenZero()
    else     -> doSomethingWhenNegative()
}

是的......有一些(最小的)代码重复。

有些语言(Ruby ?!)试图为任何情况提供一种超级优雅的形式 - 但有一个权衡:语言变得更复杂,更难以让程序员知道端到端。

我的2美分......

答案 1 :(得分:5)

when条件的grammar如下:

whenCondition (used by whenEntry)
  : expression
  : ("in" | "!in") expression
  : ("is" | "!is") type
  ;

这意味着您只能将isin用作不必是完整表达式的特殊情况;其他一切都必须是正常的表达。由于> 0不是有效表达式,因此无法编译。

此外,范围在Kotlin中已关闭,因此您无法逃避尝试使用无限范围。

相反,您应该将when语句与完整表达式一起使用,如示例所示:

when {
    foo > 0 -> doSomethingWhenPositive()
    foo < 0 -> doSomethingWhenNegative()
    else -> doSomethingWhenZero()
}

或者:

when {
    foo < 0 -> doSomethingWhenNegative()
    foo == 0 -> doSomethingWhenZero()        
    foo > 0 -> doSomethingWhenPositive()        
}

可能更具可读性。

答案 2 :(得分:1)

您希望代码优雅,为什么要留在when表达式上。 Kotlin足够灵活,可以使用扩展来构建一个新的。

首先,我们应声明我们只能在此处传递Comparable<T>,因为您必须比较该值。

然后,我们有了我们的框架:

fun <T: Comparable<T>> case(target: T, tester: Tester<T>.() -> Unit) {
    val test = Tester(target)
    test.tester()
    test.funFiltered?.invoke() ?: return
}
class Tester<T : Comparable<T>>(val it: T) {
    var funFiltered: (() -> Unit)? = null
    infix operator fun Boolean.minus(block: () -> Unit) {
        if (this && funFiltered == null) funFiltered = block
    }

    fun lt(arg: T) = it < arg
    fun gt(arg: T) = it > arg
    fun ge(arg: T) = it >= arg
    fun le(arg: T) = it <= arg
    fun eq(arg: T) = it == arg
    fun ne(arg: T) = it != arg
    fun inside(arg: Collection<T>) = it in arg
    fun inside(arg: String) = it as String in arg
    fun outside(arg: Collection<T>) = it !in arg
    fun outside(arg: String) = it as String !in arg
}

之后我们可以拥有优雅的代码,如:

case("g") {
    (it is String) - { println("hello") } // normal comparison, like `is`
    outside("gg") - { println("gg again") } // invoking the contains method
}

case(233) {
    lt(500) - { println("less than 500!") }
    // etc.
}

如果您满意,可以将minus功能重命名为compareTo并返回0.这样,您可以将-替换为=> ,看起来像scala。

答案 3 :(得分:0)

我们可以使用let来实现此行为。

response.code().let {
    when {
        it == 200 -> handleSuccess()
        it == 401 -> handleUnauthorisedError()
        it >= 500 -> handleInternalServerError()
        else -> handleOtherErrors()
    }
}

希望这会有所帮助

答案 4 :(得分:-1)

我发现了一种小技巧,可以帮助您将大于,小于或任何其他表达式与其他表达式混合。 简单来说,Kotlin中的when语句查看“ case”,如果它是一个范围,它将查看变量是否在该范围内,但是如果不是,它将查看该案例是否属于同一类型变量,如果不是,则会出现语法错误。因此,要解决此问题,您可以执行以下操作:

when (foo) {
    if(foo > 0) foo else 5 /*or any other out-of-range value*/ -> doSomethingWhenPositive()
    in -10000..0   -> doSomethingWhenBetweenNegativeTenKAndZero()
    if(foo < -10000) foo else -11000 -> doSomethingWhenNegative()
}

如您所见,这利用了Kotlin中的所有内容都是表达式的事实。因此,IMO,在此功能被添加到语言中之前,这是一个非常好的解决方案。