Котлінський умовний оператор Котліна


Відповіді:


617

У Котліні 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
буле

2
@MikeRylander Я продовжив відповідь, щоб зробити це явним. Дякуємо, що вказали на це.
Дрю Ноакс

1
@AdeelAnsari Ні, це не виправляє. Це гірше. Порівняйте це. b + if (a) c else dпроти b + (c if (a) else d)останнього потрібні додаткові дужки. тому що cне додається умовою і else.
Наетмул

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
nullbyte

3
Використовуйте <T: Будь-яке>, інакше це буде неправильно:true then { null } ?: "not-null"
Євген Петренко

BTW, ?:оператор тут 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конструкції, які використовуються в Котліні, коли перевіряються умови. Це також спосіб виразити каскади if-else альтернативним способом. Далі відповідає прикладу ОТ.

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

Розширення

Як показує багато хороших прикладів ( Котлінський термінальний умовний оператор ) в інших відповідях, розширення також можуть допомогти у вирішенні випадку використання.


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конструкція накладки


34
Це не так важливо для питання, але чому б не використовувати ?: , оператор elvis ? Перша функція буде замінена на e.getMessage() ?: "unknown". Другий може бути виражений якobj?.lastMessage?.timestamp ?: { Date() }()
гаряча клавіша

1
@hotkey для цього немає ніякої спеціальної мети. З моєї точки зору, це виглядає більш послідовно і візуально менш галасливо в ланцюгових операціях, тому що ви не повинні загортати конструкцію в дужки
ruX

14
@ruX оператор elvis призначений саме для цього, і ваше використання досить незвичне.
Джейсон Мінард

6
Хоча ?: чудово, давайте не зайдемо далеко за дорогу до Perl.
Річард Хейвен

29

Ява еквівалент потрійного оператора

a ? b : c

є простим ІФ в Котліні в одному рядку

if(a) b else c

немає потрійного оператора (умова? тоді: інше), тому що звичайний, якщо працює в цій ролі добре.

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


Особливий випадок для порівняння Null

можна скористатися оператором Elvis

if ( a != null ) a else b
// equivalent to
a ?: b

28

У kotlin немає потрійного оператора , оскільки 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: Control Flow: if, коли, for, while


27

У Котліна if- це вираз, тобто він повертає значення. Тому немає потрійного оператора (condition ? then : else), тому що звичайний, якщо працює в цій ролі добре. Посібник звідси

// 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

Це стає ще коротшим у випадку, якщо c null:

b.takeIf { a }

Також зауважте, що типова для Java value != null ? value : defaultValueверсія нульових перевірок, як переводити в ідеоматичний Котлін, просто value ?: defaultValue.

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


6
Як b.takeIf { a } ?: cкоротше і читабельніше, ніж if (a) b else c? Оператор Terneray, безумовно, відсутня функція в Котліні, оскільки імена змінних і умови можуть бути довгими і змусити вас розділити лінію, яка погана
Javad Sadeqzadeh

1
Слід також зазначити, що takeIfзавжди оцінюється справжній випадок (тут a). Цей вираз не тільки може бути марно обчислений, якщо aтрапиться помилковим, але ви не зможете отримати користь від розумних анкет à la if (a is Int) { a + 3 }.
Оператор

@TheOperator, неправильно. { a }є ліниво оціненою лямбда.
Вадим

1
Я написав це неправильно, має бути "завжди оцінює справжній випадок (тут b)". Але навіть { a }, ледачи, треба оцінювати, щоб визначити результат вираження.
Оператор

24

Погляньте на документи :

У Kotlin, якщо це вираз, тобто він повертає значення. Тому немає потрійного оператора (умова? Тоді: інше), тому що звичайний, якщо працює в цій ролі добре.



12

ЗАВДАННЯ :

Розглянемо наступний приклад:

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

У Котліні нам потрібен наступний еквівалент:

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


РІШЕННЯ :

1.а . Ви можете використовувати if-expressionв Котліні:

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")
    }
}

3
Щоправда, але приклад, який ви показуєте, має whenяк вислів, а не вираз. Більш релевантним порівнянням з потрійними умовними виразами було б, щоб кожна гілка повернула значення, таке, що ціле, коли вираз оцінюється до значення (як це відбувається з потрійними умовними умовами).
Дрю Ноакс

9

У Котліні немає потрійного оператора. Це здається проблематичним на перший погляд. Але подумайте, що ми можемо це зробити з inline, якщо інше твердження, тому що це тут вираження. Просто ми повинні зробити -

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

Тут ми можемо ще, якщо заблокувати занадто багато, скільки нам потрібно. Подібно до-

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

Отже, ця лінія настільки проста і набагато читає, ніж потрійний оператор. коли ми використовуємо більше ніж один потрійний оператор у Java, це здається жахливим. Але тут ми маємо чіткий синтаксис. навіть ми можемо записати його в декількох рядках.


9

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

Ще одна гарна концепція котліна - оператор Елвіса. Вам не потрібно перевіряти нуль кожен раз.

val l = b?.length ?: -1

Це поверне довжину, якщо b не є нульовим, інакше він виконує операцію правої сторони.


7

як цитується Дрю Ноакс, котлін використовує, якщо заява як вираз, тому термінальний умовний оператор більше не потрібен,

але з функцією розширення та перевантаження інфіксації ви могли це реалізувати самостійно, ось приклад

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"

Можливо, видаліть <T> краще? infix fun або (falsy: T?) = If (flag) по-справжньому ложно
соло

1
Але додавання <T> може змусити його працювати: (клас> 80), тоді нуль або "В"
соло

Це дійсно круто, я збираюся його використовувати: P Але зауважте, що, якщо я не помиляюся, це призведе до виділення об'єктів кожного разу, коли він буде викликаний. Це не велика угода, але варто знати, що це не абстрагування з нульовою вартістю.
Адам

6

Ще одним цікавим підходом було б використання when:

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

Це може бути досить зручно в деяких складніших сценаріях. І якщо чесно, для мене це читабельніше, ніжif ... else ...


6

Це можна зробити багатьма способами в Котліні

  1. Використання if

    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

У Котліні немає потрійної операції, але є кілька цікавих способів обійти це. Як зазначали інші, прямий переклад на Котлін виглядав би так:

val x = if (condition) result1 else result2

Але особисто я думаю, що це може трохи забитися і важко читати. Є деякі інші параметри, вбудовані в бібліотеку. Ви можете використовувати takeIf {} з оператором elvis:

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

Там відбувається те, що команда takeIf {} повертає або результат1, або нуль, а оператор elvis обробляє параметр null. Є, наприклад, кілька додаткових варіантів, наприклад {}:

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 пропонує велику гнучкість у тому, як ви вирішите висловити свій код. Існує незліченна кількість варіантів моїх прикладів, і, мабуть, способів, яких я ще не знайшов. Я сподіваюся, що це допомагає!


takeIfдійсно мій улюблений варіант, дуже елегантний.
Хав'єр Мендонча

4

Пам'ятайте, що оператор Ternary та оператор Elvis мають у Котліні окремі значення, на відміну від багатьох популярних мов. Діяння expression? value1: value2дасть вам погані слова від компілятора Kotlin , на відміну від будь-якої іншої мови, оскільки в Котліні немає термінального оператора, як згадується в офіційних документах . Причина в тому, що заяви if, коли і try-catch самі повертають значення.

Отже, дію expression? value1: value2можна замінити на

val max = if (a> b) print ("Виберіть a") друку ("Select b")

Оператор Елвіса, який є у Котліна , працює лише у випадку змінних змінних, наприклад:

Якщо я роблю щось на кшталт value3 = value1 ?: value2того, якщо value1 є null, тоді value2 буде повернуто, інакше value1 буде повернуто.

Більш чітке розуміння можна досягти з цих відповідей .


3

Ви можете використовувати ifдля цього вираз у Котліні. У Котліні 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
Old Man of Aran

1

Ще один короткий підхід до використання

val value : String = "Kotlin"

value ?: ""

Тут kotlin сам перевіряє нульове значення, і якщо воно є null, воно передає значення порожнього рядка.


1

Чому можна використовувати щось подібне:

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

коли ви можете реально використовувати щось подібне ( aбуле в даному випадку):

when {
  a -> b
  else -> b
}

1
Тому що перший є семантично зрозумілим та легко зрозумілим для когось іншого, хто читає його, навіть якщо вони не знайомі з Котліном, а другий - ні.
mc01

1
Ну, ви зрозуміли, але я не можу зрозуміти, чому розробники Kotlin не ввели потрійний вираз
ZZ 5

Я думаю, що це ? and :суперечить декларування нуля / типу, а не перевірка типу. Крім цього я не бачу причин. Я думаю, що хтось напевно подумав би, якщо є вбудована перевірка стану 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

За допомогою наступних функцій infix я можу охопити багато випадків звичайного використання майже так само, як це можна зробити в 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")

Довідкові документи


0

приклад: var енергія: Int = дані? .get (позиція) ?. енергія? .toInt ()?: 0

У kotlin, якщо ви використовуєте ?: Він буде працювати, як якщо виписка повернеться до нуля, тоді ?: 0 знадобиться 0 або все, що у вас є, напишіть цю сторону.


-1

У Котліні ви можете використовувати потрійну операцію так: val x = if(a) "add b" else "add c"


1
На це питання вже дано відповідь, і останнім часом його не оновлювали. Зараз немає потреби публікувати ще одну відповідь, яка не відрізняється від попередньої.
Headcracker

-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")
    }
}

Ця версія є вільною та не вступає в конфлікт з оператором зведення нуля.


Це такий же, як відповідь девіантної, де вона названа thenзамість yes.
Ри-

@Ry так, і я не впевнений, чи є вони однаковою людиною, але ідея використовувати інфіксні методи з додатковими параметрами походить з форуму Котліна. Те, що я не бачив, - це метод "ні", який я придумав, тому що я вважаю, що вбудоване використання оператора злиття нуля заплутане, оскільки знак питання стоїть після "тодішнього значення" замість умови, як це є у більшості мов.
Брайан В. Вагнер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.