Использование предложения Kotlin WHEN для <, <=,> =,> сравнений
Я пытаюсь использовать предложение WHEN
с помощью сравнения >
или <
.
Это не компилируется. Есть ли способ использовать нормальный набор булевых операторов (< < =, >= > ) в сравнении, чтобы включить это?
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
привлекательно, но не работает, как я ожидаю для такого типа сравнения.
Ответы
Ответ 1
Даже гибкий язык, такой как Kotlin, не имеет "элегантного" /DRY -решения для каждого случая.
Вы можете написать что-то вроде:
when (foo) {
in 0 .. Int.MAX_VALUE -> doSomethingWhenPositive()
0 -> doSomethingWhenZero()
else -> doSomethingWhenNegative()
}
Но тогда вы зависите от типа переменной.
Я считаю, что следующая форма является самой идиоматической в Котлине:
when {
foo > 0 -> doSomethingWhenPositive()
foo == 0 -> doSomethingWhenZero()
else -> doSomethingWhenNegative()
}
Да... существует некоторая (минимальная) копия кода.
Некоторые языки (Ruby?!) пытались обеспечить элегантную форму uber для любого случая - но есть компромисс: язык становится более сложным и сложным для программиста, чтобы он знал конец.
Мои 2 цента...
Ответ 2
Грамматика для условия when
выглядит следующим образом:
whenCondition (used by whenEntry)
: expression
: ("in" | "!in") expression
: ("is" | "!is") type
;
Это означает, что вы можете использовать только is
или in
качестве особых случаев, которые не должны быть полным выражением; все остальное должно быть нормальным выражением. Поскольку > 0
не является допустимым выражением, оно не скомпилируется.
Кроме того, диапазоны закрыты в Котлине, поэтому вы не можете избежать попыток использовать неограниченный диапазон.
Вместо этого вы должны использовать оператор when
с полным выражением, как в вашем примере:
when {
foo > 0 -> doSomethingWhenPositive()
foo < 0 -> doSomethingWhenNegative()
else -> doSomethingWhenZero()
}
Или в качестве альтернативы:
when {
foo < 0 -> doSomethingWhenNegative()
foo == 0 -> doSomethingWhenZero()
foo > 0 -> doSomethingWhenPositive()
}
который может быть более читабельным.
Ответ 3
Вы хотите, чтобы ваш код был изящным, поэтому зачем оставаться в выражении 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.
Ответ 4
Мы можем использовать let
для достижения этого поведения.
response.code().let {
when {
it == 200 -> handleSuccess()
it == 401 -> handleUnauthorisedError()
it >= 500 -> handleInternalServerError()
}
}
Надеюсь это поможет
Ответ 5
Я нашел немного хакерский способ, который может помочь вам смешивать больше, меньше или любое другое выражение с другими в выражениях. Проще говоря, когда оператор в Kotlin просматривает "case", и, если это диапазон, он видит, находится ли переменная в этом диапазоне, но если это не так, он смотрит, имеет ли 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, это довольно хорошее решение, пока эта функция не будет добавлена к языку.