Trójliniowy operator warunkowy Kotlin


460

Jaki jest odpowiednik tego wyrażenia w Kotlinie?

a ? b : c

To nie jest poprawny kod w Kotlin.


5
Oficjalna dyskusja: dyskusji.kotlinlang.org/t/ternary-operator/2116
Vadzim

Odpowiedzi:


617

W Kotlinie ifwyrażenia są wyrażeniami. Zatem następujący kod jest równoważny:

if (a) b else c

Ważne jest tutaj rozróżnienie między wyrażeniem a stwierdzeniem. W Javie / C # / JavaScript iftworzy instrukcję, co oznacza, że ​​nie jest rozpoznawana jako wartość. Mówiąc konkretniej, nie można przypisać jej do zmiennej.

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

Jeśli pochodzisz z języka, w którym ifznajduje się wypowiedź, może się to wydawać nienaturalne, ale to uczucie wkrótce powinno ustąpić.


57
Dodatkowo możesz użyć when.
bashor

5
żeby dodać, jeśli to wyrażenie boolowskie, możesz nawet iść zx = a==b
gnomeria

2
@MikeRylander Rozszerzyłem odpowiedź, aby wyrazić to jasno. Dzięki za zwrócenie na to uwagi.
Drew Noakes,

1
@AdeelAnsari Nie, to nie jest poprawianie. Jest gorzej. Porównaj to. b + if (a) c else dvs. b + (c if (a) else d)Ten ostatni wymaga dodatkowych nawiasów. ponieważ cnie jest objęty warunkiem i else.
Naetmul

1
Oto krótka dyskusja na ten temat. talk.kotlinlang.org/t/ternary-operator/2116/141
F. Norbert

70

Możesz zdefiniować własną Booleanfunkcję rozszerzenia, która zwraca się, nullgdy Booleanma falsezapewnić strukturę podobną do operatora trójskładnikowego:

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

To by a ? b : ctłumaczyło wyrażenie a then b ?: ctak:

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

Aktualizacja: Ale aby zrobić więcej przełączników warunkowych podobnych do Javy, potrzebujesz czegoś takiego

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

println(condition then { "yes" } ?: "no") zwracaj uwagę na lambda. jego obliczanie zawartość powinna zostać odroczone do czasu upewnić conditionsiętrue

Ten wygląda na niezgrabny, dlatego istnieje duże zapotrzebowanie na przeniesienie trójskładnikowego operatora Java do Kotlin


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

3
Użyj <T: Any>, w przeciwnym razie będzie to działać niepoprawnie:true then { null } ?: "not-null"
Eugene Petrenko


64

TL; DR

if (a) b else c

to, czego możesz użyć zamiast wyrażenia operatora trójskładnikowego a ? b : c.


W Kotlin wiele wyrażeń kontrolnych, w tym if, whena nawet trymoże być używanych jako wyrażenia . Oznacza to, że mogą one mieć wynik, który można przypisać do zmiennej, zwrócony z funkcji itp.

Składniowo nie ma potrzeby trójskładnikowego operatora

W wyniku wyrażeń Kotlina język tak naprawdę nie potrzebuje operatora potrójnego .

if (a) b else c

to, czego możesz użyć zamiast wyrażenia operatora trójskładnikowego a ? b : c.

Myślę, że chodzi o to, że poprzednie wyrażenie jest bardziej czytelne, ponieważ wszyscy wiedzą, co ifelserobi, a ? :raczej niejasne, jeśli nie znasz już składni.

Niemniej jednak muszę przyznać, że często brakuje mi wygodniejszego operatora trójskładnikowego.


Inne alternatywy

kiedy

Podczas whensprawdzania warunków możesz także zobaczyć konstrukcje używane w Kotlinie. Jest to również sposób na wyrażenie alternatywnych kaskad. Poniższy przykład odpowiada OT.

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

Rozszerzenia

Jak pokazuje wiele dobrych przykładów ( Kotlin Ternary Conditional Operator ) w innych odpowiedziach, rozszerzenia mogą również pomóc w rozwiązaniu twojego przypadku użycia.


36

Dla siebie korzystam z następujących funkcji rozszerzających:

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

Pierwszy zwróci podaną wartość domyślną, jeśli obiekt jest równy null. Drugi oceni wyrażenie podane w lambda w tym samym przypadku.

Stosowanie:

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

Osobiście dla mnie kod powyżej bardziej czytelny niż ifwstawianie konstrukcji


34
To nie jest tak istotne dla pytania, ale dlaczego nie użyć ?: , Operator elvis ? Pierwsza funkcja zostanie zastąpiona przez e.getMessage() ?: "unknown". Drugi można wyrazić jakoobj?.lastMessage?.timestamp ?: { Date() }()
skrót

1
@hotkey nie ma do tego specjalnego celu. Z mojego punktu widzenia wygląda to bardziej spójnie i mniej hałaśliwie podczas operacji łańcuchowych, ponieważ nie należy owijać konstrukcji w nawiasy
ruX

14
@ruX operator Elvisa jest specjalnie do tego przeznaczony i twoje użycie jest raczej nietypowe.
Jayson Minard

6
Chociaż?: Jest w porządku, nie idźmy zbyt daleko w dół do Perla.
Richard Haven


28

W kotlin nie ma operatora trójskładnikowego , ponieważ if elseblok zwraca wartość

więc możesz zrobić: val max = if (a > b) a else b zamiast javamax = (a > b) ? b : c

Możemy również użyć whenkonstrukcji, to także zwraca wartość:

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

Oto link do dokumentacji kotlin: Control Flow: if, when, for, while


27

W Kotlinie ifjest wyrażeniem, tzn. Zwraca wartość. Dlatego nie ma operatora trójskładnikowego (condition ? then : else), ponieważ zwykły jeśli działa dobrze w tej roli. źródło ręczne stąd

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

Niektóre przypadki narożne niewymienione w innych odpowiedziach.

Od momentu pojawienia się takeIf w Kotlin 1.1 operator trójskładnikowy a ? b : cmożna również wyrazić w następujący sposób:

b.takeIf { a } ?: c

Staje się to jeszcze krótsze, gdy c wynosi null:

b.takeIf { a }

Zauważ też, że typowe w świecie Java kontrole zerowe, takie jak value != null ? value : defaultValueprzetłumaczenie ideomatycznego Kotlina na just value ?: defaultValue.

Podobne a != null ? b : cmożna przetłumaczyć na a?.let { b } ?: c.


6
Jak jest b.takeIf { a } ?: ckrótszy i bardziej czytelny niż if (a) b else c? Operator Terneray jest z pewnością brakującą cechą w Kotlin, ponieważ nazwy zmiennych i warunek mogą być długie i powodować, że dzielisz linię, która jest zła
Javad Sadeqzadeh

1
Należy również zauważyć, że takeIfzawsze ocenia prawdziwy przypadek (tutaj a). To wyrażenie może być nie tylko bezużyteczne, jeśli okaże asię fałszywe, ale nie możesz skorzystać z inteligentnych rzutów à la if (a is Int) { a + 3 }.
TheOperator,

@ TheOperator, źle. { a }to leniwie oceniana lambda.
Vadzim

1
Napisałem to źle, powinno być „zawsze ocenia prawdziwy przypadek (tutaj b)”. Ale nawet { a }leniwy musi zostać oceniony, aby określić wynik wyrażenia.
TheOperator,

24

Spójrz na dokumenty :

W Kotlinie if jest wyrażeniem, tzn. Zwraca wartość. Dlatego nie ma operatora trójskładnikowego (warunek? To: else), ponieważ zwykły jeśli działa dobrze w tej roli.



12

ZADANIE :

Rozważmy następujący przykład:

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

Potrzebujemy następującego odpowiednika w Kotlin:

return (! answer.isSuccessful ()) ? „zły” : answer.body (). string ()


ROZWIĄZANIA :

1.a . Możesz użyć if-expressionw Kotlin:

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

1.b . Może być znacznie lepiej, jeśli odwrócisz to if-expression(zróbmy to bez not):

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

2 . Operator Elvisa z Kotlin ?:może wykonać pracę jeszcze lepiej:

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

3 . Lub użyj Extension functiondla odpowiedniej Answerklasy:

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

4 . Za pomocą Extension functionmożesz zmniejszyć kod dzięki Elvis operator:

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

5 . Lub po prostu użyj whenoperatora:

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

Mam nadzieję że to pomoże.


11

po zastąpieniu operatora przełączania języków podobnych do C. W najprostszej formie wygląda to tak

when (x) {
    1 -> print("x == 1")
    2 -> print("x == 2")
    else -> {
        print("x is neither 1 nor 2")
    }
}

3
To prawda, ale pokazany przykład zawiera whenwyrażenie, a nie wyrażenie. Bardziej odpowiednie porównanie z trójskładnikowymi wyrażeniami warunkowymi polega na tym, aby każda gałąź zwróciła wartość, tak że całe wyrażenie po wyrażeniu zwraca wartość (jak ma to miejsce w przypadku trójkowych wyrażeń warunkowych).
Drew Noakes

9

W Kotlinie nie ma operatora trójskładnikowego. Na pierwszy rzut oka wydaje się to problematyczne. Ale pomyśl, że możemy to zrobić za pomocą instrukcji inline if else, ponieważ jest to wyrażenie tutaj. Po prostu musimy zrobić -

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

Tutaj możemy jeszcze zablokować zbyt wiele, ile potrzebujemy. Lubić-

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

Ta linia jest więc tak prosta i czytelna jak operator trójskładnikowy. kiedy używamy więcej niż jednego trójskładnikowego operatora w Javie, wydaje się to okropne. Ale tutaj mamy jasną składnię. nawet my możemy napisać to w wielu wierszach.


9

Możesz użyć var a= if (a) b else czamiast operatora trójskładnikowego.

Kolejną dobrą koncepcją kotlina jest Elvis. Nie musisz za każdym razem sprawdzać wartości null.

val l = b?.length ?: -1

Zwróci długość, jeśli b nie ma wartości null, w przeciwnym razie wykona instrukcję po prawej stronie.


7

jak cytował Drew Noakes, kotlin używa instrukcji if jako wyrażenia, więc Ternary Operator warunkowy nie jest już potrzebny,

ale z funkcją rozszerzenia i przeciążeniem infix możesz sam to zaimplementować, oto przykład

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
}

następnie użyj tego w ten sposób

val grade = 90
val clazz = (grade > 80) then "A" or "B"

Może usuń <T> lepiej? infix fun lub (falsy: T?) = If (flag) naprawdę else falsy
solo

1
Ale dodanie <T> może sprawić, że zadziała: (ocena> 80), a następnie null lub „B”
solo

To jest naprawdę fajne, użyję tego: P Ale zauważ, że jeśli się nie mylę, spowoduje to przydzielenie obiektu za każdym razem, gdy zostanie wywołane. Nie jest to wielka sprawa, ale warto wiedzieć, że nie jest to abstrakcja zerowego kosztu.
Adam

6

Innym ciekawym podejściem byłoby użycie when:

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

Może być bardzo przydatny w niektórych bardziej złożonych scenariuszach. I szczerze mówiąc, jest to dla mnie bardziej czytelne niżif ... else ...


6

Możesz to zrobić na wiele sposobów w Kotlinie

  1. Używanie if

    if(a) b else c
  2. Używanie kiedy

    when (a) { 
        true -> print("value b") 
        false -> print("value c") 
        else -> {  
            print("default return in any other case") 
        } 
    }
  3. Brak bezpieczeństwa

    val a = b ?: c

5

W Kotlinie nie ma operacji trójskładnikowych, ale istnieją fajne sposoby obejścia tego. Jak zauważyli inni, bezpośrednie tłumaczenie na Kotlin wyglądałoby tak:

val x = if (condition) result1 else result2

Ale osobiście uważam, że może to być trochę zagracone i trudne do odczytania. Istnieje kilka innych opcji wbudowanych w bibliotekę. Możesz użyć takeIf {} z operatorem elvis:

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

To, co się dzieje, polega na tym, że komenda takeIf {} zwraca albo wynik1 albo null, a operator elvis obsługuje opcję null. Istnieją dodatkowe opcje, takeUnless {}, na przykład:

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

Język jest jasny, wiesz, co się dzieje.

Jeśli jest to często używany warunek, możesz także zrobić coś fajnego, na przykład użyć wbudowanej metody rozszerzenia. Załóżmy, że chcemy na przykład śledzić wynik gry jako Int i chcemy zawsze zwracać 0, jeśli dany warunek nie jest spełniony:

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

Ok, to wydaje się brzydkie. Zastanów się jednak, jak to wygląda, gdy jest używane:

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

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

Jak widać, Kotlin oferuje dużą elastyczność w sposobie wyrażania kodu. Istnieją niezliczone warianty moich przykładów i prawdopodobnie sposoby, których jeszcze nie odkryłem. Mam nadzieję, że to pomoże!


takeIfto rzeczywiście moja ulubiona opcja, bardzo elegancka.
Javier Mendonça

4

Pamiętaj, że operator trójskładnikowy i operator Elvisa mają odrębne znaczenia w Kotlin, w przeciwieństwie do wielu popularnych języków. Spowodowałoby expression? value1: value2to złe słowa kompilatora Kotlin , w przeciwieństwie do innych języków, ponieważ w Kotlinie nie ma potrójnego operatora, jak wspomniano w oficjalnych dokumentach . Powodem jest to, że same instrukcje if, kiedy i try-catch zwracają wartości.

Tak więc robienie expression? value1: value2można zastąpić

val max = if (a> b) print („Choose a”) else print („Choose b”)

Operator Elvis że Kotlin ma, działa tylko w przypadku wartości pustych zmiennych np .:

Jeśli zrobić coś jak value3 = value1 ?: value2wtedy, gdy wartosc1 jest zerowa następnie value2 zostaną zwrócone w przeciwnym razie wartosc1 będą zwracane.

Dzięki tym odpowiedziom można lepiej zrozumieć .


3

Możesz użyć ifdo tego wyrażenia w Kotlinie. W Kotlinie ifjest wyrażenie z wartością wynikową. W Kotlinie możemy pisać

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

a w Javie możemy osiągnąć to samo, ale z większym kodem

int max(int a, int b) {
return a > b ? a : b
}

2

Jeśli nie używasz standardowej notacji, możesz ją również utworzyć / symulować za pomocą infix z czymś takim:

utwórz klasę, aby utrzymać swój cel i wynik:

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

utwórz niektóre funkcje poprawki, aby zasymulować operację potrójną

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
}

Następnie będziesz mógł użyć go w następujący sposób:

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"

Aby było całkowicie równoważne z rzeczywistym operatorem trójskładnikowym, wartościami docelowymi może być również lambda dostarczająca T
Old Man of Aran

1

Kolejne krótkie podejście do użycia

val value : String = "Kotlin"

value ?: ""

Tutaj sam kotlin sprawdza wartość zerową, a jeśli jest zerowy, to przekazuje pustą wartość ciągu.


1

Dlaczego miałby używać czegoś takiego:

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

kiedy możesz użyć czegoś takiego ( aw tym przypadku jest to boolean):

when {
  a -> b
  else -> b
}

1
Ponieważ pierwszy jest semantycznie czysty i zrozumiały dla kogoś, kto go czyta, nawet jeśli nie jest zaznajomiony z Kotlinem, podczas gdy drugi nie.
mc01,

1
Cóż, masz rację, jednak nie rozumiem, dlaczego programiści Kotlin nie wprowadzili potrójnego wyrażenia
ZZ 5

Myślę, że jest to ? and :sprzeczne z deklaracją zerowania / typu, a nie z kontrolą typu. Poza tym nie widzę żadnego powodu. Myślę, że ktoś na pewno by się nad tym zastanowił, jeśli istnieje wbudowana kontrola warunku „jeśli-inaczej”. Poczekajmy i zobaczymy w przyszłych wersjach.
bh4r4th

1

Podczas pracy z funkcją apply () wydaje się bardzo przydatna w przypadku operacji trójkowych, ponieważ jest bardziej elegancka i daje ci miejsce

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

Dzięki następującym funkcjom poprawki mogę objąć wiele typowych przypadków użycia w taki sam sposób, jak w Pythonie:

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

W Kotlinie nie ma operatora trójskładnikowego, najbardziej zamknięte są poniższe dwa przypadki,

  • Jeśli jeszcze jako wyrażenie wyrażenia

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

  • Operator Elvisa

Jeśli wyrażenie po lewej stronie?: Nie jest puste, operator elvis zwraca je, w przeciwnym razie zwraca wyrażenie po prawej. Zauważ, że wyrażenie po prawej stronie jest oceniane tylko wtedy, gdy lewa strona jest pusta.

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

Dokumenty referencyjne


0

przykład: var energy: Int = data? .get (position) ?. energy? .toInt ()?: 0

W kotlin, jeśli używasz ?: To będzie działało tak, jakby instrukcja zwróciła null, to ?: 0 zajmie 0 lub cokolwiek, co masz napisać po tej stronie.


-1

W Kotlin możesz użyć operacji trójskładnikowej w następujący sposób: val x = if(a) "add b" else "add c"


1
Odpowiedź na to pytanie jest wystarczająca i nie była ostatnio aktualizowana. Nie ma potrzeby publikowania teraz innej odpowiedzi, która nie różni się od wcześniejszych.
Headcracker

-2

Po kilku badaniach innych pomysłów, wyprowadziłem następujący operator trójskładnikowy:

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

Przykład (uruchom tutaj ):

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

Ta wersja jest płynna i nie koliduje z zerowym operatorem koalescencji.


To jest trochę tak samo jak odpowiedź devianta, w której jest nazwana thenzamiast yes.
Ry-

@ Tak, i nie jestem pewien, czy to ta sama osoba, ale pomysł użycia metod infix z opcjonalnymi pochodzi z forum Kotlin. To, czego nie widziałem, to metoda „nie”, którą wymyśliłem, ponieważ uważam, że użycie w trybie zerowym operatora koalescencyjnego jest mylące, ponieważ znak zapytania występuje po „wartości wtedy” zamiast warunku takiego jak w większości języków.
Bryan W. Wagner
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.