Котлинский троичный условный оператор

462

Что является эквивалентом этого выражения в Kotlin?

a ? b : c

Это неверный код в Котлине.

Дрю Ноакс
источник
5
Официальное обсуждение: обсуждения.kotlinlang.org/ t/ternary-operator/2116
Вадим

Ответы:

619

В Kotlin ifзаявления являются выражениями. Таким образом, следующий код эквивалентен:

if (a) b else c

Здесь важно различие между выражением и утверждением. В Java / C # / JavaScript ifформирует утверждение, означающее, что оно не преобразуется в значение. Конкретнее, вы не можете присвоить его переменной.

// Valid Kotlin, but invalid Java/C#/JavaScript
var v = if (a) b else c

Если вы говорите на языке, на котором ifесть утверждение, это может показаться неестественным, но это чувство скоро должно исчезнуть.

Дрю Ноакс
источник
57
Дополнительно вы можете использовать when.
Башор
5
просто чтобы добавить, если это логическое выражение, вы даже можете пойти сx = a==b
gnomeria
2
@MikeRylander Я расширил ответ, чтобы сделать это явным. Спасибо за указание на это.
Дрю Ноакс
1
@AdeelAnsari Нет, это не исправляет. Это хуже. Сравните это. b + if (a) c else dvs. b + (c if (a) else d)Последний требует дополнительных скобок. потому что cне включено в условие и else.
Naetmul
1
Вот небольшая дискуссия на эту тему. обсуждения.kotlinlang.org/t/ternary-operator/2116/141
Ф. Норберт
70

Вы можете определить свою собственную Booleanфункцию расширения , которая возвращается , nullкогда Booleanявляется falseобеспечить структуру , подобную троичной оператора:

infix fun <T> Boolean.then(param: T): T? = if (this) param else null

Это сделало бы a ? b : cвыражение переводящим a then b ?: c, вот так:

println(condition then "yes" ?: "no")

Обновление: Но чтобы сделать еще один Java-подобный условный переход, вам понадобится что-то подобное

infix fun <T> Boolean.then(param: () -> T): T? = if (this) param() else null

println(condition then { "yes" } ?: "no") обратите внимание на лямбду. расчет его содержание должно быть отложено , пока мы не уверены , conditionявляетсяtrue

Это выглядит неуклюже, поэтому существует высокий спрос на портирование троичного оператора Java в Котлин

отклоняющееся
источник
1
infix inline fun<T> Boolean.then(param: ()->T):T? = if(this) param() else null
ноль байт
3
Используйте <T: Any>, иначе это будет работать некорректно:true then { null } ?: "not-null"
Евгений Петренко
Кстати, ?:оператор здесь elvis-operator: kotlinlang.org/docs/reference/null-safety.html#elvis-operator
Эрик Ван
64

TL; DR

if (a) b else c

это то, что вы можете использовать вместо выражения троичного оператора a ? b : c.


В Котлин, многие управляющие операторы в том числе if, whenили даже tryмогут быть использованы в качестве выражения . Это означает, что у них может быть результат, который можно присвоить переменной, вернуть из функции и т. Д.

Синтаксически, нет необходимости в троичном операторе

В результате выражений Котлина языку на самом деле не нужен троичный оператор .

if (a) b else c

это то, что вы можете использовать вместо выражения троичного оператора a ? b : c.

Я думаю, что идея в том, что первое выражение более читабельно, так как все знают, что ifelseделает, тогда ? :как довольно непонятно, если вы уже не знакомы с синтаксисом.

Тем не менее, я должен признать, что мне часто не хватает более удобного троичного оператора.


Другие альтернативы

когда

Вы также можете увидеть whenконструкции, используемые в Kotlin при проверке условий. Это также способ выразить каскад if-else альтернативным способом. Следующее соответствует примеру OTs.

when(a) {
    true -> b
    false -> c
}

расширения

Как показывают многие хорошие примеры ( Kotlin Ternary Conditional Operator ) в других ответах, расширения также могут помочь в решении вашего варианта использования.

s1m0nw1
источник
36

Для себя я использую следующие функции расширения:

fun T?.or<T>(default: T): T = if (this == null) default else this 
fun T?.or<T>(compute: () -> T): T = if (this == null) compute() else this

Первый возвращает возвращаемое значение по умолчанию, если объект равен нулю. Второй будет оценивать выражение, предоставленное в лямбда в том же случае.

Применение:

1) e?.getMessage().or("unknown")
2) obj?.lastMessage?.timestamp.or { Date() }

Лично для меня код выше более ifчитабелен, чем встроенная конструкция

Rux
источник
34
Это не имеет отношения к вопросу, но почему бы не использовать ?: , оператор elvis ? Первая функция будет заменена на e.getMessage() ?: "unknown". Второе можно выразить какobj?.lastMessage?.timestamp ?: { Date() }()
горячая клавиша
1
@ Хоткей, для этого нет особой цели. С моей точки зрения, это выглядит более последовательным и визуально менее шумным в цепных операциях, так как вы не должны
заключать
14
@ruX оператор elvis специально для этого, и ваше использование довольно необычно.
Джейсон Минард
6
Пока?: Хорошо, давайте не будем заходить слишком далеко по дороге в Perl.
Ричард Хейвен
29

Java-эквивалент троичного оператора

a ? b : c

простой IF в Котлине в одну строку

if(a) b else c

нет троичного оператора (условие? тогда: еще), потому что обычный, если работает отлично в этой роли.

https://kotlinlang.org/docs/reference/control-flow.html#if-expression


Особый случай для нулевого сравнения

Вы можете использовать оператор Элвиса

if ( a != null ) a else b
// equivalent to
a ?: b
Рэймонд Шенон
источник
28

В котлине нет троичного оператора , так как if elseблок возвращает значение

Итак, вы можете сделать: val max = if (a > b) a else b вместо Javamax = (a > b) ? b : c

Мы также можем использовать whenконструкцию, она также возвращает значение:

val max = when(a > b) {
    true -> a
    false -> b
}

Вот ссылка на документацию kotlin: Поток управления: если, когда, для, пока

romiope
источник
27

В Kotlin ifэто выражение, т.е. оно возвращает значение. Поэтому нет тройного оператора (condition ? then : else), потому что обычный if отлично работает в этой роли. ручной источник отсюда

// Traditional usage 
var max = a 
if (a < b) max = b

// With else 
var max: Int
if (a > b) {
    max = a
} else {
    max = b
}

// As expression 
val max = if (a > b) a else b
Крис Руф
источник
26

Некоторые угловые случаи, не упомянутые в других ответах.

С момента появления takeIf в Kotlin 1.1 троичный оператор a ? b : cтакже может быть выражен так:

b.takeIf { a } ?: c

Это становится еще короче, если с null:

b.takeIf { a }

Также обратите внимание, что типичные в мире Java проверки value != null ? value : defaultValueна ноль, такие как перевод в идеологическом Kotlin, просто value ?: defaultValue.

Подобное a != null ? b : cможно перевести на a?.let { b } ?: c.

Vadzim
источник
6
Как b.takeIf { a } ?: cкороче и читабельнее, чем if (a) b else c? Тернерный оператор, безусловно, отсутствует в Kotlin, так как имена переменных и условия могут быть длинными, и вы можете разбить строку, что плохо
Джавад Садекзаде
1
Следует также отметить, что takeIfвсегда оценивает истинный случай (здесь a). Мало того, что это выражение может быть вычислено бесполезно, если оно aокажется ложным, но вы не сможете извлечь выгоду из умных приведений а-ля if (a is Int) { a + 3 }.
Оператор
@ Оператор, не так. { a }это лениво оцененная лямбда.
Вадим
1
Я написал это неправильно, должно быть "всегда оценивает истинный случай (здесь b)". Но даже { a }, будучи ленивым, нужно оценить, чтобы определить результат выражения.
Оператор
24

Посмотрите на документы :

В Kotlin if является выражением, то есть возвращает значение. Поэтому нет тернарного оператора (условие? Тогда: еще), потому что обычный if отлично работает в этой роли.

Ли Ин
источник
13

Ява

int temp = a ? b : c;

Эквивалент Котлину:

var temp = if (a) b else c
doubleThunder
источник
12

ЗАДАЧА :

Давайте рассмотрим следующий пример:

if (!answer.isSuccessful()) {
    result = "wrong"
} else {
    result = answer.body().string()
}
return result

Нам нужен следующий эквивалент в Kotlin:

return (! answer.isSuccessful ()) ? «неправильно» : answer.body (). string ()


РЕШЕНИЯ :

1.а . Вы можете использовать if-expressionв Kotlin:

return if (!answer.isSuccessful()) "wrong" else answer.body().string()

. Может быть намного лучше, если вы перевернете это if-expression(давайте сделаем это без not):

return if (answer.isSuccessful()) answer.body().string() else "wrong"

2 . Оператор Котлина Элвис ?:может сделать работу еще лучше:

return answer.body()?.string() ?: "wrong"

3 . Или используйте Extension functionдля соответствующего Answerкласса:

fun Answer.bodyOrNull(): Body? = if (isSuccessful()) body() else null

4 . Используя Extension functionвы можете уменьшить код благодаря Elvis operator:

return answer.bodyOrNull()?.string() ?: "wrong"

5 . Или просто используйте whenоператор:

when (!answer.isSuccessful()) {
    parseInt(str) -> result = "wrong"
    else -> result = answer.body().string()
}

Надеюсь это поможет.

Энди
источник
11

когда заменяет оператор переключения C-подобных языков. В простейшем виде это выглядит так

when (x) {
    1 -> print("x == 1")
    2 -> print("x == 2")
    else -> {
        print("x is neither 1 nor 2")
    }
}
Guruprasath
источник
3
Верно, но пример, который вы показываете, имеет whenформу выражения, а не выражения. Более уместным сравнением с троичными условными выражениями было бы, чтобы каждая ветвь возвращала значение, такое, чтобы целое выражение выражало значение (как это происходит с троичными условными выражениями).
Дрю Ноакс
9

В Котлине нет троичного оператора. Это кажется проблематичным на первый взгляд. Но думаю, что мы можем сделать это с помощью встроенного оператора if else, потому что это выражение здесь. Просто мы должны сделать -

var number = if(n>0) "Positive" else "Negetive"

Здесь мы можем еще, если заблокировать слишком много, сколько нам нужно. Подобно-

var number = if(n>0) "Positive" else if(n<0) "Negative" else "Zero"

Так что эта строка настолько проста и намного удобочитаемее, чем троичный оператор. когда мы используем более одного троичного оператора в Java, это кажется ужасным. Но здесь у нас есть четкий синтаксис. даже мы можем написать это в несколько строк тоже.

HM Nayem
источник
9

Вы можете использовать var a= if (a) b else cвместо троичного оператора.

Другая хорошая концепция kotlin - оператор Элвиса. Вам не нужно проверять ноль каждый раз.

val l = b?.length ?: -1

Это вернет длину, если b не равно нулю, иначе он выполнит оператор правой части.

Android Geek
источник
7

как цитировал Дрю Ноакс, kotlin использует оператор if в качестве выражения, поэтому тернарный условный оператор больше не нужен,

но с помощью функции расширения и инфиксной перегрузки вы можете реализовать это самостоятельно, вот пример

infix fun <T> Boolean.then(value: T?) = TernaryExpression(this, value)

class TernaryExpression<out T>(val flag: Boolean, val truly: T?) {
    infix fun <T> or(falsy: T?) = if (flag) truly else falsy
}

затем используйте это так

val grade = 90
val clazz = (grade > 80) then "A" or "B"
Minami
источник
Может быть, удалить <T> лучше? инфиксное веселье или (falsy: T?) = If (flag) действительно еще falsy
соло
1
Но добавление <T> может заставить его работать: (класс> 80), затем ноль или "B"
соло
Это действительно круто, я собираюсь использовать это: P Но учтите, что, если я не ошибаюсь, это будет вызывать распределение объектов каждый раз, когда он вызывается. Не большая сделка, но стоит знать, что это не абстракция с нулевой стоимостью.
Адам
6

Еще один интересный подход будет использовать when:

when(a) {
  true -> b
  false -> b
}

Может быть весьма удобно в некоторых более сложных сценариях. И, честно говоря, это более читабельно для меня, чемif ... else ...

Гжегож Пивоварек
источник
6

Вы можете сделать это по-разному в Kotlin

  1. Используя если

    if(a) b else c
  2. Использование когда

    when (a) { 
        true -> print("value b") 
        false -> print("value c") 
        else -> {  
            print("default return in any other case") 
        } 
    }
  3. Нулевая безопасность

    val a = b ?: c
Раджеш Далсания
источник
5

В Котлине нет никакой троичной операции, но есть несколько забавных способов обойти это. Как отмечали другие, прямой перевод на Kotlin будет выглядеть так:

val x = if (condition) result1 else result2

Но лично я думаю, что это может стать немного загроможденным и трудным для чтения. Есть несколько других опций, встроенных в библиотеку. Вы можете использовать takeIf {} с оператором elvis:

val x = result1.takeIf { condition } ?: result2

Здесь происходит то, что команда takeIf {} возвращает либо ваш result1, либо null, а оператор elvis обрабатывает нулевую опцию. Есть несколько дополнительных опций, takeUnless {}, например:

val x = result1.takeUnless { condition } ?: result2

Язык понятен, вы знаете, что он делает.

Если это часто используемое условие, вы также можете сделать что-нибудь интересное, например, использовать метод встроенного расширения. Давайте предположим, что мы хотим отслеживать счет игры как, например, Int, и мы хотим всегда возвращать 0, если данное условие не выполняется:

inline fun Int.zeroIfFalse(func: () -> Boolean) : Int = if (!func.invoke()) 0 else this     

Хорошо, это выглядит некрасиво. Но посмотрим, как это выглядит, когда он используется:

var score = 0
val twoPointer = 2
val threePointer = 3

score += twoPointer.zeroIfFalse { scoreCondition } 
score += threePointer.zeroIfFalse { scoreCondition } 

Как вы можете видеть, Kotlin предлагает большую гибкость при выборе способа выражения вашего кода. Есть бесчисленное множество вариантов моих примеров и, возможно, способов, которые я еще даже не обнаружил. Надеюсь, это поможет!

pranalli
источник
takeIfэто действительно мой любимый вариант, очень элегантный.
Хавьер Мендонса
4

Помните, что тернарный оператор и оператор Элвиса имеют разные значения в котлинском языке, в отличие от многих популярных языков. Выполнение expression? value1: value2дало бы вам плохие слова от компилятора Kotlin , в отличие от любого другого языка, поскольку в Kotlin нет ни одного троичного оператора, как упомянуто в официальных документах . Причина в том, что операторы if, when и try-catch сами возвращают значения.

Таким образом, делать expression? value1: value2можно заменить

val max = if (a> b) print («Выбрать a») else print («Выбрать b»)

Оператор Элвиса, который есть у Котлина , работает только в случае обнуляемых переменных, например:

Если я что - то вроде value3 = value1 ?: value2то , если значение1 является нулевым , то значение2 будет возвращен в противном случае значение1 будут возвращены.

Из этих ответов можно достичь более четкого понимания .

Neeraj Sewani
источник
3

Вы можете использовать ifвыражение для этого в Kotlin. В Kotlin ifесть выражение со значением результата. Так что в Котлине мы можем написать

fun max(a: Int, b: Int) = if (a > b) a else b

и в Java мы можем добиться того же, но с большим кодом

int max(int a, int b) {
return a > b ? a : b
}
Гульзар Бхат
источник
2

Если вам не нужно использовать стандартную нотацию, вы также можете создать / смоделировать ее, используя infix, примерно так:

создайте класс для хранения вашей цели и результата:

data class Ternary<T>(val target: T, val result: Boolean)

создать некоторые инфиксные функции для симуляции троичной операции

infix fun <T> Boolean.then(target: T): Ternary<T> {
    return Ternary(target, this)
}

infix fun <T> Ternary<T>.or(target: T): T {
    return if (this.result) this.target else target
}

Тогда вы сможете использовать его так:

val collection: List<Int> = mutableListOf(1, 2, 3, 4)

var exampleOne = collection.isEmpty() then "yes" or "no"
var exampleTwo = (collection.isNotEmpty() && collection.contains(2)) then "yes" or "no"
var exampleThree = collection.contains(1) then "yes" or "no"
Эди Контрерас
источник
Чтобы он был полностью эквивалентен фактическому троичному оператору, целевыми значениями также может быть лямбда, снабжающая T
Старик Арана
1

Еще один короткий подход к использованию

val value : String = "Kotlin"

value ?: ""

Здесь kotlin сам проверяет нулевое значение, и если он равен нулю, он передает пустое строковое значение.

Винод Паттаншетти
источник
1

Зачем использовать что-то вроде этого:

when(a) {
  true -> b
  false -> b
}

когда вы можете использовать что-то вроде этого ( aв данном случае это логическое значение):

when {
  a -> b
  else -> b
}
ZZ 5
источник
1
Потому что первый семантически понятен и легко понятен кому-то, кто читает его, даже если он не знаком с Котлином, а второй - нет.
mc01
1
Ну, вы поняли, но я не могу понять, почему разработчики Kotlin не представили троичное выражение
ZZ 5
Я думаю, что это ? and :противоречит декларации типа nullable / type, а не проверке типа. Кроме этого я не вижу никакой причины. Я думаю, что кто-то определенно мог бы подумать, если есть встроенная проверка условий if-else. Давайте подождем и посмотрим в будущих версиях.
bh4r4th
1

При работе с apply (), пусть кажется очень удобным при работе с троичными операциями, так как это более элегантно и дает вам место

val columns: List<String> = ...
val band = Band().apply {
    name = columns[0]
    album = columns[1]
    year = columns[2].takeIf { it.isNotEmpty() }?.let { it.toInt() } ?: 0
}
Хуан Мендес
источник
0

Со следующими инфиксными функциями я могу охватить многие распространенные случаи использования почти так же, как это можно сделать в Python:

class TestKotlinTernaryConditionalOperator {

    @Test
    fun testAndOrInfixFunctions() {
        Assertions.assertThat(true and "yes" or "no").isEqualTo("yes")
        Assertions.assertThat(false and "yes" or "no").isEqualTo("no")

        Assertions.assertThat("A" and "yes" or "no").isEqualTo("yes")
        Assertions.assertThat("" and "yes" or "no").isEqualTo("no")

        Assertions.assertThat(1 and "yes" or "no").isEqualTo("yes")
        Assertions.assertThat(0 and "yes" or "no").isEqualTo("no")

        Assertions.assertThat(Date() and "yes" or "no").isEqualTo("yes")
        @Suppress("CAST_NEVER_SUCCEEDS")
        Assertions.assertThat(null as Date? and "yes" or "no").isEqualTo("no")
    }
}

infix fun <E> Boolean?.and(other: E?): E? = if (this == true) other else null
infix fun <E> CharSequence?.and(other: E?): E? = if (!(this ?: "").isEmpty()) other else null
infix fun <E> Number?.and(other: E?): E? = if (this?.toInt() ?: 0 != 0) other else null
infix fun <E> Any?.and(other: E?): E? = if (this != null) other else null
infix fun <E> E?.or(other: E?): E? = this ?: other
Николас Корнетт
источник
0

В Котлине нет троичного оператора, наиболее закрытыми являются следующие два случая,

  • Если еще как выражение

val a = true if(a) print("A is true") else print("A is false")

  • Элвис оператор

Если выражение слева от?: Не равно нулю, оператор elvis возвращает его, в противном случае возвращает выражение справа. Обратите внимание, что выражение в правой части вычисляется только в том случае, если значение в левой части равно нулю.

 val name = node.getName() ?: throw IllegalArgumentException("name expected")

Справочные документы

JTeam
источник
0

пример: var energy: Int = data? .get (position) ?. energy? .toInt ()?: 0

В kotlin, если вы используете ?: Это будет работать так, как если бы инструкция вернула ноль тогда ?: 0 это займет 0 или что-то еще, что вы написали на этой стороне.

Абхилаша Ядав
источник
-1

В Kotlin вы можете использовать троичную операцию следующим образом: val x = if(a) "add b" else "add c"

manuelernest0
источник
1
На этот вопрос уже достаточно ответа, и недавно он не обновлялся. Теперь нет необходимости размещать другой ответ, который не отличается от предыдущих.
Хедрекер
-2

После некоторых исследований других идей я получил следующий троичный оператор:

infix fun <T : Any> Boolean.yes(trueValue: T): T? = if (this) trueValue else null
infix fun <T : Any> T?.no(falseValue: T): T = this ?: falseValue

Пример (запустить здесь ):

fun main() {
    run {
        val cond = true
        val result = cond yes "True!" no "False!"
        println("ternary test($cond): $result")
    }
    run {
        val cond = false
        val result = cond yes "True!" no "False!"
        println("ternary test($cond): $result")
    }
}

Эта версия свободно и не конфликтует с оператором null coalescing.

Брайан В. Вагнер
источник
Это почти то же самое, что и ответ девианта, где thenвместо имени он назван yes.
Ry-
@ Да, и я не уверен, что это один и тот же человек, но идея использовать инфиксные методы с опциями приходит с форума Kotlin. То, что я не видел, - это метод «нет», который я придумал, потому что я нахожу сбивающим с толку использование оператора объединения нулей, потому что вопросительный знак стоит после «тогда значения» вместо условия, как в большинстве языков.
Брайан В. Вагнер