Operador condicional ternario de Kotlin

Respuestas:

619

En Kotlin, las ifdeclaraciones son expresiones. Entonces el siguiente código es equivalente:

if (a) b else c

La distinción entre expresión y declaración es importante aquí. En Java / C # / JavaScript, ifforma una declaración, lo que significa que no se resuelve en un valor. Más concretamente, no puede asignarlo a una variable.

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

Si vienes de un idioma donde ifhay una declaración, esto puede parecer antinatural, pero ese sentimiento debería desaparecer pronto.

Drew Noakes
fuente
57
Además puedes usar when.
bashor
55
solo para agregar, si se trata de una expresión booleana, incluso puede ir conx = a==b
gnomeria
2
@MikeRylander He extendido la respuesta para hacer esto explícito. Gracias por señalar esto.
Drew Noakes
1
@AdeelAnsari No, no está rectificando. Es peor. Compara esto. b + if (a) c else dvs. b + (c if (a) else d)El último requiere paréntesis adicionales. porque cno está encerrado por la condición y else.
Naetmul
1
Aquí hay una pequeña discusión sobre este tema. discus.kotlinlang.org/t/ternary-operator/2116/141
F. Norbert
70

Puede definir su propia Booleanfunción de extensión que se devuelve nullcuando Booleanes falseproporcionar una estructura similar al operador ternario:

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

Esto haría que una a ? b : cexpresión se traduzca a a then b ?: c, así:

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

Actualización: Pero para hacer un cambio condicional más similar a Java, necesitará algo así

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

println(condition then { "yes" } ?: "no") Presta atención a la lambda. su cálculo de contenido debe posponerse hasta que nos aseguremos de que conditionseatrue

Este parece torpe, es por eso que existe una gran demanda para portar el operador ternario de Java a Kotlin

desviado
fuente
1
infix inline fun<T> Boolean.then(param: ()->T):T? = if(this) param() else null
nullbyte
3
Use <T: Any>, de lo contrario funcionará incorrectamente:true then { null } ?: "not-null"
Eugene Petrenko
Por cierto, el ?:operador aquí es elvis-operator: kotlinlang.org/docs/reference/null-safety.html#elvis-operator
Eric Wang
64

TL; DR

if (a) b else c

es lo que puede usar en lugar de la expresión del operador ternario a ? b : c.


En Kotlin, muchas declaraciones de control incluyen if , wheno incluso tryse pueden usar como expresiones . Esto significa que esos pueden tener un resultado que puede asignarse a una variable, devolverse de una función, etc.

Sintácticamente, no hay necesidad de operador ternario

Como resultado de las expresiones de Kotlin, el lenguaje no necesita realmente el operador ternario .

if (a) b else c

es lo que puede usar en lugar de la expresión del operador ternario a ? b : c.

Creo que la idea es que la expresión anterior es más legible ya que todos saben qué ifelse hace, mientras no ? :está claro si aún no está familiarizado con la sintaxis.

Sin embargo, debo admitir que a menudo extraño el operador ternario más conveniente.


Otras alternativas

cuando

También puede ver whenconstrucciones utilizadas en Kotlin cuando se verifican las condiciones. También es una forma de expresar cascadas if-else en una forma alternativa. Lo siguiente corresponde al ejemplo de OT.

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

Extensiones

Como muestran muchos buenos ejemplos ( Operador condicional ternario de Kotlin ) en las otras respuestas, las extensiones también pueden ayudarlo a resolver su caso de uso.

s1m0nw1
fuente
36

Para mí uso las siguientes funciones de extensión:

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

El primero devolverá el valor predeterminado proporcionado en caso de que el objeto sea igual a nulo. El segundo evaluará la expresión proporcionada en lambda en el mismo caso.

Uso:

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

Personalmente, para mí, el código anterior es más legible que la ifconstrucción en línea

ruX
fuente
34
No es tan relevante para la pregunta, pero ¿por qué no usar ?: , El operador de elvis ? La primera función sería reemplazada por e.getMessage() ?: "unknown". El segundo puede expresarse comoobj?.lastMessage?.timestamp ?: { Date() }()
tecla
1
@hotkey no hay un propósito especial para eso. Desde mi punto de vista es apariencia más consistentes y visualmente menos ruidosas en las operaciones de la cadena, ya que no debe envolver la construcción en los soportes
Rux
14
@ruX el operador de elvis es específicamente para esto y su uso es bastante inusual.
Jayson Minard
66
Mientras?: Está bien, no vayamos demasiado lejos por el camino a Perl.
Richard Haven
28

No hay operador ternario en kotlin, ya que el if elsebloque devuelve valor

entonces, puedes hacer: en val max = if (a > b) a else b lugar de Javamax = (a > b) ? b : c

También podemos usar whenconstrucción, también devuelve valor:

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

Aquí hay un enlace para la documentación de kotlin: Flujo de control: if, when, for, while

romiope
fuente
27

En Kotlin, ifes una expresión, es decir, devuelve un valor. Por lo tanto, no existe un operador ternario (condition ? then : else), porque ordinario si funciona bien en este rol. fuente manual desde aquí

// 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
Kris Roofe
fuente
26

Algunos casos de esquina no mencionados en otras respuestas.

Desde la aparición de takeIf en Kotlin 1.1, el operador ternario a ? b : ctambién puede expresarse así:

b.takeIf { a } ?: c

Esto se vuelve aún más corto en caso de que c sea null:

b.takeIf { a }

También tenga en cuenta que las comprobaciones nulas típicas en el mundo Java, como value != null ? value : defaultValuetraducir en Kotlin ideomático a just value ?: defaultValue.

Similar a != null ? b : cse puede traducir a a?.let { b } ?: c.

Vadzim
fuente
66
¿Cómo es b.takeIf { a } ?: cmás corto y más legible que if (a) b else c? Terneray operador es sin duda una característica que falta en Kotlin ya que los nombres de variable y la condición puede ser largo y que se divide la línea de lo que es malo
Javad Sadeqzadeh
1
También debe tenerse en cuenta que takeIfsiempre evalúa el caso verdadero (aquí a). No solo se puede calcular esa expresión inútilmente si aresulta ser falsa, sino que no se puede beneficiar de los lanzamientos inteligentes a la carta if (a is Int) { a + 3 }.
TheOperator
@TheOperator, mal. { a }es una lambda perezosamente evaluada.
Vadzim
1
Lo escribí mal, debería ser "siempre evalúa el caso verdadero (aquí b)". Pero incluso { a }, aunque vago, debe evaluarse para determinar el resultado de la expresión.
TheOperator
24

Echa un vistazo a los documentos :

En Kotlin, si es una expresión, es decir, devuelve un valor. Por lo tanto, no hay operador ternario (condición? Entonces: más), porque ordinario si funciona bien en este rol.

Li Ying
fuente
13

Java

int temp = a ? b : c;

Equivalente a Kotlin:

var temp = if (a) b else c
doble trueno
fuente
12

TAREA :

Consideremos el siguiente ejemplo:

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

Necesitamos el siguiente equivalente en Kotlin:

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


SOLUCIONES :

1.a . Puedes usar if-expressionen Kotlin:

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

1.b . Puede ser mucho mejor si cambias esto if-expression(hagámoslo sin not):

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

2 . El operador de Kotlin en Elvis ?:puede hacer un trabajo aún mejor:

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

3 . O use un Extension functionpara la Answerclase correspondiente :

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

4 . Usando el Extension functionpuede reducir un código gracias a Elvis operator:

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

5 . O simplemente use el whenoperador:

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

Espero que esto ayude.

Andy
fuente
11

cuando reemplaza el operador de cambio de lenguajes tipo C. En la forma más simple se ve así

when (x) {
    1 -> print("x == 1")
    2 -> print("x == 2")
    else -> {
        print("x is neither 1 nor 2")
    }
}
Guruprasath
fuente
3
Es cierto, pero el ejemplo que muestra tiene whencomo una declaración, no una expresión. Una comparación más relevante con las expresiones condicionales ternarias sería hacer que cada rama devuelva un valor, de modo que la expresión completa cuando se evalúe como un valor (como sucede con los condicionales ternarios).
Drew Noakes
9

No hay operador ternario en Kotlin. Parece problemático a primera vista. Pero piense que podemos hacerlo con la instrucción inline if else porque esta es la expresión aquí. Simplemente tenemos que hacer -

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

Aquí podemos hacerlo si bloqueamos tantos como necesitemos. Me gusta-

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

Entonces, esta línea es tan simple y mucho más legible que el operador ternario. Cuando utilizamos más de un operador ternario en Java, parece horrible. Pero aquí tenemos una sintaxis clara. incluso podemos escribirlo en varias líneas también.

HM Nayem
fuente
9

Puede usar var a= if (a) b else cen lugar del operador ternario.

Otro buen concepto de kotlin es el operador de Elvis. No necesita marcar nulo cada vez.

val l = b?.length ?: -1

Esto devolverá la longitud si b no es nulo; de lo contrario, se ejecuta la declaración del lado derecho.

Android Geek
fuente
7

como Drew Noakes citó, kotlin usa if enunciado como expresión, por lo que el Operador condicional ternario ya no es necesario,

pero con la función de extensión y la sobrecarga de infijo, puede implementarlo usted mismo, aquí hay un ejemplo

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
}

entonces úsalo así

val grade = 90
val clazz = (grade > 80) then "A" or "B"
Minami
fuente
Tal vez elimine <T> mejor? diversión infija o (falso: T?) = Si (marcar) realmente otro falso
solo
1
Pero agregar <T> puede hacerlo funcionar: (grado> 80) luego nulo o "B"
solo
Esto es realmente genial, lo voy a usar: P Pero tenga en cuenta que, a menos que me equivoque, causará una asignación de objetos cada vez que se llame. No es un gran problema, pero vale la pena saber que no es una abstracción de costo cero.
Adam
6

Otro enfoque interesante sería usar when:

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

Puede ser bastante útil en algunos escenarios más complejos. Y honestamente, es más legible para mí queif ... else ...

Grzegorz Piwowarek
fuente
6

Puedes hacerlo de muchas maneras en Kotlin

  1. Usando if

    if(a) b else c
  2. Usando cuando

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

    val a = b ?: c
Rajesh Dalsaniya
fuente
5

No hay una operación ternaria en Kotlin, pero hay algunas formas divertidas de solucionarlo. Como otros han señalado, una traducción directa a Kotlin se vería así:

val x = if (condition) result1 else result2

Pero, personalmente, creo que eso puede ser un poco abarrotado y difícil de leer. Hay algunas otras opciones integradas en la biblioteca. Puede usar takeIf {} con un operador de elvis:

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

Lo que sucede allí es que el comando takeIf {} devuelve su resultado1 o nulo, y el operador elvis maneja la opción nula. Hay algunas opciones adicionales, takeUnless {}, por ejemplo:

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

El lenguaje es claro, ya sabes lo que está haciendo.

Si es una condición de uso común, también podría hacer algo divertido como usar un método de extensión en línea. Supongamos que queremos rastrear un puntaje de juego como Int, por ejemplo, y queremos devolver siempre 0 si no se cumple una condición determinada:

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

Ok, eso parece feo. Pero considere cómo se ve cuando se usa:

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

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

Como puede ver, Kotlin ofrece mucha flexibilidad en cómo elige expresar su código. Hay innumerables variaciones de mis ejemplos y probablemente formas que aún no he descubierto. ¡Espero que esto ayude!

pranalli
fuente
takeIfEs mi opción favorita de hecho, muy elegante.
Javier Mendonça
4

Recuerde que el operador ternario y el operador de Elvis tienen significados separados en Kotlin a diferencia de muchos idiomas populares. Hacerlo expression? value1: value2te daría malas palabras por el compilador de Kotlin , a diferencia de cualquier otro idioma, ya que no hay un operador ternario en Kotlin como se menciona en los documentos oficiales . La razón es que las propias declaraciones if, when y try-catch devuelven valores.

Entonces, hacer expression? value1: value2puede ser reemplazado por

val max = if (a> b) print ("Elija a") más print ("Elija b")

El operador de Elvis que tiene Kotlin , funciona solo en el caso de variables anulables, p. Ej .:

Si hago algo parecido value3 = value1 ?: value2a continuación, si valor1 es nula entonces valor2 se devolvería lo contrario valor1 se devolvería.

Se puede lograr una comprensión más clara de estas respuestas .

Neeraj Sewani
fuente
3

Puede usar la ifexpresión para esto en Kotlin. En Kotlin ifhay una expresión con un valor de resultado. Entonces en Kotlin podemos escribir

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

y en Java podemos lograr lo mismo pero con un código más grande

int max(int a, int b) {
return a > b ? a : b
}
Gulzar Bhat
fuente
2

Si no sabe qué usar la notación estándar, también puede crearla / simularla usando infijo con algo como esto:

crea una clase para mantener tu objetivo y resultado:

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

crear algunas funciones de infijo para simular una operación ternaria

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
}

Entonces podrás usarlo así:

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"
Eudy Contreras
fuente
Para que sea completamente equivalente a un operador ternario real, los valores objetivo también pueden ser lambda suministrando T
Old Man of Aran
1

Otro enfoque corto para usar

val value : String = "Kotlin"

value ?: ""

Aquí kotlin comprueba el valor nulo y, si es nulo, pasa un valor de cadena vacío.

Vinod Pattanshetti
fuente
1

¿Por qué uno usaría algo como esto:

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

cuando realmente puedes usar algo como esto ( aes booleano en este caso):

when {
  a -> b
  else -> b
}
ZZ 5
fuente
1
Porque el primero es semánticamente claro y fácilmente comprensible para alguien más que lo lea, incluso si no están familiarizados con Kotlin, mientras que el segundo no.
mc01
1
Bueno, tienes el punto, sin embargo, no puedo entender por qué los desarrolladores de Kotlin no introdujeron la expresión ternaria
ZZ 5
Creo que ? and :contradice la declaración de tipo nulo / nulo en lugar de una verificación de tipo. Aparte de eso, no veo ninguna razón. Creo que alguien definitivamente habría pensado un poco, si hay una verificación en línea de la condición if-else. Esperemos y veamos en futuras versiones.
bh4r4th
1

Cuando trabaje con apply (), deje que parezca muy útil cuando se trata de operaciones ternarias, ya que es más elegante y le da espacio

val columns: List<String> = ...
val band = Band().apply {
    name = columns[0]
    album = columns[1]
    year = columns[2].takeIf { it.isNotEmpty() }?.let { it.toInt() } ?: 0
}
Juan Mendez
fuente
0

Con las siguientes funciones de infijo puedo cubrir muchos casos de uso comunes de la misma manera que se puede hacer en 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
Nicolas Cornette
fuente
0

No hay operador ternario en Kotlin, los más cerrados son los dos casos siguientes,

  • Si más como declaración de expresión

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

  • Operador de Elvis

Si la expresión a la izquierda de?: No es nula, el operador elvis la devuelve; de ​​lo contrario, devuelve la expresión a la derecha. Tenga en cuenta que la expresión del lado derecho se evalúa solo si el lado izquierdo es nulo.

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

Documentos de referencia

JTeam
fuente
0

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

En kotlin si está usando ?: Funcionará como si la declaración devuelva nulo, entonces ?: 0 tomará 0 o lo que sea que tenga escrito en este lado.

abhilasha Yadav
fuente
-1

En Kotlin puedes usar operaciones ternarias como esta: val x = if(a) "add b" else "add c"

manuelernest0
fuente
1
Esta pregunta ya se ha respondido lo suficiente y no se ha actualizado recientemente. No es necesario publicar ahora otra respuesta que no difiera de las anteriores.
Headcracker
-2

Después de investigar algunas otras ideas, he derivado el siguiente operador ternario:

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

Ejemplo (ejecutar aquí ):

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

Esta versión es fluida y no entra en conflicto con el operador de fusión nulo.

Bryan W. Wagner
fuente
Esto es algo así como la respuesta del desviado donde se nombra en thenlugar de yes.
Ry-
@Ry sí, y no estoy seguro de si son la misma persona, pero la idea de usar métodos infix con opcionales proviene del foro de Kotlin. Lo que no he visto es el método 'no' que se me ocurrió porque encuentro confuso el uso en línea del operador de fusión nula porque el signo de interrogación está detrás del 'valor entonces' en lugar de la condición como está en la mayoría de los idiomas.
Bryan W. Wagner