Kotlin Ternary Conditional Operator


Antworten:


617

In Kotlin sind ifAnweisungen Ausdrücke. Der folgende Code ist also äquivalent:

if (a) b else c

Die Unterscheidung zwischen Ausdruck und Aussage ist hier wichtig. Bildet in Java / C # / JavaScript ifeine Anweisung, dh, sie wird nicht in einen Wert aufgelöst. Genauer gesagt können Sie es keiner Variablen zuweisen.

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

Wenn Sie aus einer Sprache kommen, in der ifes sich um eine Aussage handelt, mag dies unnatürlich erscheinen, aber dieses Gefühl sollte bald nachlassen.


57
Zusätzlich können Sie verwenden when.
Bashor

5
Nur um hinzuzufügen, wenn es ein boolescher Ausdruck ist, können Sie sogar mitx = a==b
Gnomeria

2
@ MikeRylander Ich habe die Antwort erweitert, um dies explizit zu machen. Vielen Dank für den Hinweis.
Drew Noakes

1
@AdeelAnsari Nein, es wird nicht korrigiert. Es ist schlimmer. Vergleichen Sie dies. b + if (a) c else dvs. b + (c if (a) else d)Letzteres erfordert zusätzliche Klammern. weil cist nicht von der Bedingung und eingeschlossen else.
Naetmul

1
Hier ist eine kleine Diskussion zu diesem Thema. diskutieren.kotlinlang.org/t/ternary-operator/2116/141
F. Norbert

70

Sie könnten Ihre eigene definieren BooleanErweiterungsfunktion , dass die Renditen , nullwenn die Booleanist falseeine Struktur ähnlich den ternären Operator zur Verfügung zu stellen:

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

Dies würde einen a ? b : cAusdruck a then b ?: cwie folgt übersetzen lassen :

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

Update: Aber um einen Java-ähnlichen bedingten Wechsel durchzuführen, benötigen Sie so etwas

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

println(condition then { "yes" } ?: "no") Achten Sie auf das Lambda. Die Berechnung des Inhalts sollte verschoben werden, bis wir sicher conditionsind , dass dies der Fall isttrue

Dieser sieht ungeschickt aus, weshalb eine stark nachgefragte Anforderung besteht, den ternären Java-Operator nach Kotlin zu portieren


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

3
Verwenden Sie <T: Any>, sonst funktioniert das nicht richtig:true then { null } ?: "not-null"
Eugene Petrenko

Übrigens ist der ?:Betreiber hier elvis-operator: kotlinlang.org/docs/reference/null-safety.html#elvis-operator
Eric Wang

64

TL; DR

if (a) b else c

ist das, was Sie anstelle des ternären Operatorausdrucks verwenden können a ? b : c.


In Kotlin, viele Steueranweisungen einschließlich if, whenoder sogar trykann verwendet werden als Ausdrücke . Dies bedeutet, dass diese ein Ergebnis haben können, das einer Variablen zugewiesen, von einer Funktion usw. zurückgegeben werden kann.

Syntaktisch ist kein ternärer Operator erforderlich

Aufgrund von Kotlins Ausdrücken benötigt die Sprache den ternären Operator nicht wirklich .

if (a) b else c

ist das, was Sie anstelle des ternären Operatorausdrucks verwenden können a ? b : c.

Ich denke, die Idee ist, dass der frühere Ausdruck besser lesbar ist, da jeder weiß, was er ifelsetut, während er ? :eher unklar ist, wenn Sie mit der Syntax noch nicht vertraut sind.

Trotzdem muss ich zugeben, dass ich oft den bequemeren ternären Operator vermisse .


Andere Alternativen

wann

Möglicherweise werden auch whenKonstrukte in Kotlin verwendet, wenn die Bedingungen überprüft werden. Es ist auch eine Möglichkeit, If-else-Kaskaden auf alternative Weise auszudrücken. Das Folgende entspricht dem OTs-Beispiel.

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

Erweiterungen

Wie viele gute Beispiele ( Kotlin Ternary Conditional Operator ) in den anderen Antworten zeigen, können Erweiterungen auch bei der Lösung Ihres Anwendungsfalls hilfreich sein.


36

Für mich selbst benutze ich folgende Erweiterungsfunktionen:

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

Zuerst wird der angegebene Standardwert zurückgegeben, falls das Objekt gleich null ist. Zweitens wird der Ausdruck, der in Lambda bereitgestellt wird, im selben Fall bewertet.

Verwendungszweck:

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

Persönlich für mich Code oben besser lesbar als ifKonstruktion Inlining


34
Es ist nicht so relevant für die Frage, aber warum nicht verwenden ?: , Der elvis-Operator ? Die erste Funktion würde durch ersetzt e.getMessage() ?: "unknown". Die zweite kann ausgedrückt werden alsobj?.lastMessage?.timestamp ?: { Date() }()
Hotkey

1
@hotkey dafür gibt es keinen besonderen Zweck. Aus meiner Sicht sieht es im Kettenbetrieb konsistenter und optisch weniger laut aus, da Sie die Konstruktion nicht in die Klammern
einwickeln

14
@ruX der elvis-Operator ist speziell dafür und Ihre Verwendung ist eher ungewöhnlich.
Jayson Minard

6
Während ?: Ist in Ordnung, gehen wir nicht zu weit die Straße hinunter nach Perl.
Richard Haven


28

In Kotlin gibt es keinen ternären Operator , da der if elseBlock einen Wert zurückgibt

Sie können also val max = if (a > b) a else b Folgendes tun: anstelle von Javamax = (a > b) ? b : c

Wir können auch whenKonstruktion verwenden, es gibt auch Wert zurück:

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

Hier ist der Link zur Kotlin-Dokumentation: Kontrollfluss: wenn, wann, für, während


27

In Kotlin ifist ein Ausdruck, dh er gibt einen Wert zurück. Daher gibt es keinen ternären Operator (condition ? then : else), da normal, wenn in dieser Rolle gut funktioniert. manuelle Quelle von hier

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

Einige Eckfälle werden in anderen Antworten nicht erwähnt.

Seit dem Erscheinen von takeIf in Kotlin 1.1 kann der ternäre Operator a ? b : cauch folgendermaßen ausgedrückt werden:

b.takeIf { a } ?: c

Dies wird noch kürzer, wenn c ist null:

b.takeIf { a }

Beachten Sie auch, dass in der Java-Welt typische Null-Checks wie das value != null ? value : defaultValueÜbersetzen im ideomatischen Kotlin in Just value ?: defaultValue.

Ähnliches a != null ? b : ckann übersetzt werden a?.let { b } ?: c.


6
Wie ist b.takeIf { a } ?: ckürzer und lesbarer als if (a) b else c? Der Terneray-Operator ist sicherlich ein fehlendes Feature in Kotlin, da Variablennamen und die Bedingung lang sein können und Sie die schlechte Zeile teilen können
Javad Sadeqzadeh

1
Es sollte auch beachtet werden, dass takeIfimmer der wahre Fall bewertet wird (hier a). Dieser Ausdruck kann nicht nur nutzlos berechnet werden, wenn aer falsch ist, sondern Sie können auch nicht von Smart Casts à la profitieren if (a is Int) { a + 3 }.
TheOperator

@ TheOperator, falsch. { a }ist ein träge ausgewertetes Lambda.
Vadzim

1
Ich habe es falsch geschrieben, sollte sein "bewertet immer den wahren Fall (hier b)". Aber auch { a }wenn es faul ist, muss es ausgewertet werden, um das Ergebnis des Ausdrucks zu bestimmen.
TheOperator

24

Schauen Sie sich die Dokumente an :

Wenn in Kotlin ein Ausdruck vorhanden ist, gibt er einen Wert zurück. Daher gibt es keinen ternären Operator (Bedingung? Dann: sonst), weil gewöhnlich, wenn in dieser Rolle gut funktioniert.



12

AUFGABE :

Betrachten wir das folgende Beispiel:

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

Wir brauchen das folgende Äquivalent in Kotlin:

return (! answer.isSuccessful ()) ? "falsch" : answer.body (). string ()


LÖSUNGEN :

1.a . Sie können if-expressionin Kotlin verwenden:

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

1.b . Es kann viel besser sein, wenn Sie dies umdrehen if-expression(lassen Sie es uns ohne tun not):

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

2 . Kotlins Elvis-Betreiber ?:kann noch besser arbeiten:

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

3 . Oder verwenden Sie ein Extension functionfür die entsprechende AnswerKlasse:

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

4 . Mit dem können Extension functionSie einen Code reduzieren dank Elvis operator:

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

5 . Oder verwenden Sie einfach den whenOperator:

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

Hoffe das hilft.


11

Ersetzt den Switch-Operator von C-ähnlichen Sprachen. In der einfachsten Form sieht es so aus

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

3
Stimmt, aber das Beispiel, das Sie zeigen, hat wheneine Aussage, keinen Ausdruck. Ein relevanterer Vergleich mit ternären bedingten Ausdrücken wäre, dass jeder Zweig einen Wert zurückgibt, so dass der gesamte When-Ausdruck einen Wert ergibt (wie dies bei ternären Bedingungen der Fall ist).
Drew Noakes

9

In Kotlin gibt es keinen ternären Operator. Es scheint auf den ersten Blick problematisch. Aber denken Sie, wir können es mit der Inline-if-else-Anweisung tun, da dies hier Ausdruck ist. Einfach müssen wir tun -

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

Hier können wir sonst zu viele blockieren, wie wir brauchen. Mögen-

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

Diese Zeile ist also so einfach und gut lesbar als der ternäre Operator. Wenn wir in Java mehr als einen ternären Operator verwenden, scheint dies schrecklich. Aber hier haben wir eine klare Syntax. Auch wir können es in mehreren Zeilen schreiben.


9

Sie können var a= if (a) b else canstelle des ternären Operators verwenden.

Ein weiteres gutes Konzept von Kotlin ist Elvis Operater. Sie müssen nicht jedes Mal null überprüfen.

val l = b?.length ?: -1

Dies gibt die Länge zurück, wenn b nicht null ist, andernfalls wird die Anweisung auf der rechten Seite ausgeführt.


7

Wie Drew Noakes zitiert, verwendet Kotlin die if-Anweisung als Ausdruck, sodass ein ternärer bedingter Operator nicht mehr erforderlich ist.

Mit der Erweiterungsfunktion und der Infix-Überladung können Sie dies jedoch selbst implementieren. Hier ein Beispiel

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
}

dann benutze es so

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

Vielleicht entfernen Sie <T> besser? Infix Spaß oder (Falsy: T?) = If (Flag) wirklich sonst Falsy
Solo

1
Aber mit <T> kann es funktionieren: (Klasse> 80) dann null oder "B"
Solo

Das ist wirklich cool, ich werde es verwenden: P Beachten Sie jedoch, dass es bei jedem Aufruf zu einer Objektzuweisung kommt, sofern ich mich nicht irre. Keine große Sache, aber es lohnt sich zu wissen, dass es sich nicht um eine Null-Kosten-Abstraktion handelt.
Adam

6

Ein weiterer interessanter Ansatz wäre when:

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

Kann in einigen komplexeren Szenarien sehr praktisch sein. Und ehrlich gesagt ist es für mich lesbarer alsif ... else ...


6

In Kotlin kann man das auf viele Arten machen

  1. Verwenden von if

    if(a) b else c
  2. Verwenden wann

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

    val a = b ?: c

5

Es gibt keine ternäre Operation in Kotlin, aber es gibt einige unterhaltsame Möglichkeiten, dies zu umgehen. Wie andere betont haben, würde eine direkte Übersetzung in Kotlin folgendermaßen aussehen:

val x = if (condition) result1 else result2

Aber ich persönlich denke, das kann etwas unübersichtlich und schwer zu lesen sein. In die Bibliothek sind einige andere Optionen integriert. Sie können takeIf {} mit einem elvis-Operator verwenden:

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

Dort passiert, dass der Befehl takeIf {} entweder Ihr Ergebnis1 oder null zurückgibt und der elvis-Operator die Option null verarbeitet. Es gibt einige zusätzliche Optionen, takeUnless {}, zum Beispiel:

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

Die Sprache ist klar, Sie wissen, was das tut.

Wenn es sich um eine häufig verwendete Bedingung handelt, können Sie auch eine unterhaltsame Methode wie die Verwendung einer Inline-Erweiterungsmethode verwenden. Nehmen wir an, wir möchten beispielsweise ein Spielergebnis als Int verfolgen und immer 0 zurückgeben, wenn eine bestimmte Bedingung nicht erfüllt ist:

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

Ok, das scheint hässlich. Aber überlegen Sie, wie es aussieht, wenn es verwendet wird:

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

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

Wie Sie sehen können, bietet Kotlin viel Flexibilität bei der Auswahl Ihres Codes. Es gibt unzählige Variationen meiner Beispiele und wahrscheinlich Wege, die ich noch nicht einmal entdeckt habe. Ich hoffe das hilft!


takeIfist meine Lieblingsoption in der Tat sehr elegant.
Javier Mendonça

4

Denken Sie daran, dass der ternäre Operator und der Elvis-Operator in Kotlin im Gegensatz zu vielen gängigen Sprachen unterschiedliche Bedeutungen haben. Wenn expression? value1: value2Sie dies tun, würde der Kotlin- Compiler im Gegensatz zu jeder anderen Sprache schlechte Worte liefern , da es in Kotlin keinen ternären Operator gibt, wie in den offiziellen Dokumenten erwähnt . Der Grund dafür ist, dass die Anweisungen if, when und try-catch selbst Werte zurückgeben.

Tun expression? value1: value2kann also durch ersetzt werden

val max = wenn (a> b) drucken ("Wähle a") sonst drucke ("Wähle b")

Der Elvis Operator dass Kotlin hat, funktioniert nur im Fall von Nullable - Variablen ex .:

Wenn ich so etwas tun value3 = value1 ?: value2dann , wenn Wert1 ist null dann Wert2 sonst zurückgegeben werden Wert1 würden zurückgegeben.

Aus diesen Antworten kann ein klareres Verständnis erzielt werden .


3

Sie können ifhierfür in Kotlin einen Ausdruck verwenden. In Kotlin ifist ein Ausdruck mit einem Ergebniswert. Also können wir in Kotlin schreiben

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

und in Java können wir dasselbe erreichen, aber mit größerem Code

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

2

Wenn Sie die Standardnotation nicht verwenden, können Sie sie auch mithilfe eines Infixes wie folgt erstellen / simulieren :

Erstellen Sie eine Klasse, um Ihr Ziel und Ergebnis zu halten:

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

Erstellen Sie einige Infix-Funktionen, um eine ternäre Operation zu simulieren

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
}

Dann können Sie es folgendermaßen verwenden:

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"

Damit es einem tatsächlichen ternären Operator vollständig entspricht, können die Zielwerte auch Lambda sein, das T
Old Man of Aran

1

Ein weiterer kurzer Ansatz

val value : String = "Kotlin"

value ?: ""

Hier prüft kotlin selbst den Nullwert und wenn es null ist, übergibt es einen leeren Zeichenfolgenwert.


1

Warum sollte man so etwas benutzen:

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

wenn Sie so etwas tatsächlich verwenden können ( aist in diesem Fall boolesch):

when {
  a -> b
  else -> b
}

1
Weil der erste semantisch klar und für andere, die ihn lesen, leicht verständlich ist, auch wenn er mit Kotlin nicht vertraut ist, während der zweite nicht.
mc01

1
Nun, Sie haben verstanden, aber ich kann nicht verstehen, warum Kotlin-Entwickler keinen ternären Ausdruck eingeführt haben
ZZ 5

Ich denke, ? and :widerspricht eher der Deklaration nullable / type als einer Typprüfung. Abgesehen davon sehe ich keinen Grund. Ich denke, jemand hätte definitiv darüber nachgedacht, wenn es eine Inline-If-else-Zustandsprüfung gibt. Lassen Sie uns abwarten und in zukünftigen Versionen sehen.
bh4r4th

1

Wenn Sie mit apply () arbeiten, scheint let sehr praktisch zu sein, wenn Sie mit ternären Operationen arbeiten, da es eleganter ist und Ihnen Raum gibt

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

Mit den folgenden Infix-Funktionen kann ich viele gängige Anwendungsfälle so behandeln, wie es in Python möglich ist:

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

In Kotlin gibt es keinen ternären Operator. Die beiden am häufigsten abgeschlossenen Fälle sind:

  • Wenn sonst als Ausdrucksanweisung

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

  • Elvis-Betreiber

Wenn der Ausdruck links von ?: Nicht null ist, gibt der elvis-Operator ihn zurück, andernfalls gibt er den Ausdruck rechts zurück. Beachten Sie, dass der Ausdruck auf der rechten Seite nur ausgewertet wird, wenn der Ausdruck auf der linken Seite null ist.

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

Referenzdokumente


0

Beispiel: var energy: Int = data? .get (position)?. energy? .toInt () ?: 0

In kotlin, wenn Sie ?: Verwenden , funktioniert es so, als ob die Anweisung dann null zurückgibt ?: 0 nimmt 0 oder was auch immer Sie auf dieser Seite geschrieben haben.


-1

In Kotlin können Sie die ternäre Operation folgendermaßen verwenden: val x = if(a) "add b" else "add c"


1
Diese Frage wurde bereits ausreichend beantwortet und kürzlich nicht aktualisiert. Es ist nicht nötig, jetzt eine andere Antwort zu posten, die sich nicht von früheren unterscheidet.
Headcracker

-2

Nach einigen Recherchen anderer Ideen habe ich den folgenden ternären Operator abgeleitet:

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

Beispiel ( hier ausführen ):

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

Diese Version ist fließend und steht nicht in Konflikt mit dem Null-Koaleszenz-Operator.


Dies entspricht in etwa der Antwort von deviant, bei der der Name thenanstelle von lautet yes.
Ry-

@ Ry ja, und ich bin nicht sicher, ob sie dieselbe Person sind, aber die Idee, Infix-Methoden mit Optionen zu verwenden, stammt aus dem Kotlin-Forum. Was ich nicht gesehen habe, ist die "Nein" -Methode, die ich mir ausgedacht habe, weil ich die Inline-Verwendung des Null-Koaleszenz-Operators verwirrend finde, weil das Fragezeichen nach dem "Dann-Wert" steht, anstatt wie in den meisten Sprachen.
Bryan W. Wagner
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.