Saltar al contenido
K
Kotlin: el lenguajePaso a paso30 min de lectura · 5 min de máquina Intermedio

Condiciones: if y when

Al terminar: Decidir qué hace el programa con if, else y when, y combinar condiciones con && y ||.

Al terminar sabrás hacer que el programa decida con preguntas de sí o no. Con eso tendrás la primera regla del torneo en código: puntosPor(golesAFavor, golesEnContra), que dice cuántos puntos gana un equipo con un marcador.

Importa porque casi todo el torneo es esto: una función que recibe datos y decide.

Temas: condiciones (>, ==, &&, ||, !); if y when, y cómo se devuelve un valor desde ellos. En la lección siguiente, Bucles y tu primer test, repetirás cosas con bucles y comprobarás esta regla con un test.

Condiciones: preguntas de sí o no

Para que un programa decida, primero tiene que poder preguntar. Una condición es una pregunta cuya respuesta es sí o no, es decir, un valor Boolean (true o false, que ya conoces de Variables, tipos y operaciones). Se construyen comparando valores o combinando otras condiciones:

Se escribePreguntaEjemploDa
> y <¿mayor que? ¿menor que?3 > 2true
>= y <=¿mayor o igual? ¿menor o igual?3 >= 3true
==¿es igual?3 == 4false
!=¿es distinto?3 != 4true
&&¿se cumplen las dos? («y»)3 > 2 && 3 < 5true
||¿se cumple al menos una? («o»)3 > 5 || 3 > 2true
!lo contrario!(3 > 2)false

Ojo con el =: un signo guarda un valor en una variable (val edad = 15) y dos signos preguntan si dos valores son iguales (edad == 15).

ejemplo mínimo: comparar y combinar (con comentarios)
fun main() {
    val edad = 15
    val conAutorizacion = true
    println(edad > 18)                        // false: ¿mayor que?
    println(edad >= 15)                       // true: ¿mayor o igual?
    println(edad == 15)                       // true: ¿igual? (dos signos; uno solo guarda un valor)
    println(edad != 15)                       // false: ¿distinto?
    println(edad >= 18 || conAutorizacion)    // true: || es «o»: basta con que una sea verdad
    println(edad >= 12 && conAutorizacion)    // true: && es «y»: tienen que serlo las dos
    println(!conAutorizacion)                 // false: ! le da la vuelta
}
false
true
true
false
true
true
false

if: decidir entre dos caminos

if (condición) { … } else { … } ejecuta el primer bloque si la condición es verdadera y el segundo si no. La parte else if (otra condición) añade más caminos; el else final recoge el resto.

Un if puede usarse de dos formas. Antes, la diferencia entre instrucción y expresión: una instrucción hace algo (imprimir, guardar), y una expresión da un valor (x * 2, edad > 18). El if puede ser lo primero, ejecutar una cosa u otra, y también lo segundo: lo que valga la rama elegida es lo que devuelve el if, y lo puedes guardar en una variable, pasar a println o devolver desde una función.

ejemplo mínimo: if como instrucción y como expresión (con comentarios)
fun main() {
    val puntos = 3
 
    // if como instrucción: ejecuta una cosa u otra, no da ningún valor
    if (puntos > 1) {
        println("Varios puntos")
    } else {
        println("Un punto o ninguno")
    }
 
    // if como expresión: lo que valga la rama elegida se guarda en la variable
    val texto = if (puntos > 1) "puntos" else "punto"
    println("Tienes $puntos $texto")
 
    // también dentro de una llamada, y en cadena con else if
    println(if (puntos == 0) "Sin puntos" else if (puntos < 3) "Pocos" else "Muchos")
 
    // con llaves, el valor de la rama es su última línea (aquí, el texto)
    val mensaje = if (puntos >= 3) {
        val extra = puntos - 3
        "Tres o más (sobran $extra)"
    } else {
        "Menos de tres"
    }
    println(mensaje)
}
Varios puntos
Tienes 3 puntos
Muchos
Tres o más (sobran 0)

Como el if tiene que dar un valor en cualquier caso, el else es obligatorio cuando lo usas como expresión.

when: elegir entre muchos caminos

Cuando hay más de dos caminos, una cadena de else if se hace larga; when la ordena. Cada rama se escribe lo que se pregunta -> el valor que se da: a la izquierda de la flecha -> va la condición y a la derecha el valor que devuelve esa rama. Tiene dos formas. Sin sujeto, when { … }, cada rama es una condición completa y se aplica la primera que sea verdadera: es la que usarás para la regla de los puntos. Con sujeto, when (minuto) { … }, compara ese valor con cada rama. Como el if, when también da un valor.

ejemplo mínimo: when sin sujeto y con sujeto (con comentarios)
// Sin sujeto: cada rama es una condición completa; gana la primera que sea verdadera
fun clima(temperatura: Int): String = when {
    temperatura < 10 -> "frío"
    temperatura < 25 -> "templado"
    else -> "calor"                       // si ninguna anterior encaja, esta
}
 
// Con sujeto: compara minuto con cada rama; «in 0..45» significa «está entre 0 y 45»
fun tramo(minuto: Int): String = when (minuto) {
    in 0..45 -> "primer tiempo"
    in 46..90 -> "segundo tiempo"
    else -> "prórroga"
}
 
fun main() {
    println(clima(5))
    println(clima(20))
    println(clima(31))
    val minuto = 47
    val etiqueta = if (minuto > 45) "segundo" else "primero"
    println("$etiqueta: ${tramo(minuto)}")
}
frío
templado
calor
segundo: segundo tiempo

in 0..45 es un rango, los números del 0 al 45; los rangos se explican en Bucles y tu primer test. De momento léelo como «está entre 0 y 45».

Sin else, cada una de estas dos formas falla al compilar. Con val x = if (minuto > 45) 3 sale 'if' must have both main and 'else' branches when used as an expression., y con un when incompleto, 'when' expression must be exhaustive. Add an 'else' branch.

Las funciones clima y tramo de arriba son de expresión: el when es todo el cuerpo y su valor es el resultado. Con llaves hay que decir qué se devuelve con return: puede ir delante del when entero, o en cada rama de unos if sueltos.

ejemplo mínimo: la misma función con llaves y return (con comentarios)
// Con llaves: return delante del when entero (devuelve lo que valga la rama que encaje)
fun climaConLlaves(temperatura: Int): String {
    return when {
        temperatura < 10 -> "frío"
        temperatura < 25 -> "templado"
        else -> "calor"
    }
}
 
// Con if sueltos: cada return devuelve y termina la función; si no se cumple ninguno, llega al último
fun climaConIf(temperatura: Int): String {
    if (temperatura < 10) {
        return "frío"        // si entra aquí, la función acaba y lo de abajo no se ejecuta
    }
    if (temperatura < 25) {
        return "templado"
    }
    return "calor"           // el caso que queda: obligatorio, o no compila
}
 
fun main() {
    println(climaConLlaves(30))   // calor
    println(climaConIf(5))        // frío
    println(climaConIf(18))       // templado
}
calor
frío
templado

Si una función declara un tipo de retorno (: Int, : String) y algún camino acaba sin return, no compila: por eso el último return de climaConIf es obligatorio.

En resumen: con la forma corta (= y la expresión, sin llaves), if y when dan el valor directamente. Con llaves, la función necesita return: uno delante del when entero, o uno en cada camino de los if sueltos.

Reto · Ejercicio: el parque de atracciones

En el Kotlin Playground (el mismo de Tu primer proyecto en Kotlin; no toques tu proyecto), escribe tres funciones. tarifa recibe la edad de un visitante y devuelve lo que paga por un día: hasta 5 años, 0; hasta 17, 8; hasta 64, 15; a partir de 65, 10. total recibe la edad y los días que viene, que si no se dice es 1, y devuelve lo que paga en total. etiqueta recibe un precio y devuelve «gratis» si es 0 o «de pago» si no. En main, escribe una línea para las edades 4, 10, 30 y 70 (un día cada una), y otra para la edad 30 durante 3 días, pasando los días por su nombre. La consola debe mostrar:

Edad 4: 0 (gratis)
Edad 10: 8 (de pago)
Edad 30: 15 (de pago)
Edad 70: 10 (de pago)
Edad 30 durante 3 días: 45
Ver solución
solución del ejercicio
fun tarifa(edad: Int): Int = when {   // when sin sujeto: cada rama es una condición
    edad <= 5 -> 0
    edad <= 17 -> 8                   // llega aquí solo si no se cumplió la anterior
    edad <= 64 -> 15
    else -> 10                        // la rama final es obligatoria
}
 
// Valor por defecto: si no se dicen los días, es 1
fun total(edad: Int, dias: Int = 1): Int = tarifa(edad) * dias
 
fun etiqueta(precio: Int): String = if (precio == 0) "gratis" else "de pago"   // if como expresión
 
fun main() {
    println("Edad 4: ${total(4)} (${etiqueta(tarifa(4))})")      // ${…}: el resultado de una llamada, dentro del texto
    println("Edad 10: ${total(10)} (${etiqueta(tarifa(10))})")
    println("Edad 30: ${total(30)} (${etiqueta(tarifa(30))})")
    println("Edad 70: ${total(70)} (${etiqueta(tarifa(70))})")
    println("Edad 30 durante 3 días: ${total(30, dias = 3)}")     // argumento con nombre
}

Paso · puntosPor en Puntos.kt y un Main.kt que lo usa

Objetivo: añadir la regla de los puntos a Puntos.kt y que Main.kt la llame. Al terminar, ./gradlew :torneo:run imprime el torneo, sus jornadas y unos cuantos marcadores con sus puntos. Los tests de esta regla los escribes en la lección siguiente.

torneo/
kotlin+java/
com.sebastiannarvaez.marcador.torneoel paquete que ya tienes
Puntos.ktse modifica
Main.ktse modifica
Lo que tocas, en la vista Android.

1 · Abre Puntos.kt. En la vista Android, despliega torneo → kotlin+java → com.sebastiannarvaez.marcador.torneo y haz doble clic en Puntos.kt.

Piezas que vas a usar, todas de esta lección:

  • fun nombre(a: Int, b: Int): Int = …: función de expresión; lo que va tras el = es lo que devuelve.
  • when { condición -> valor … else -> valor }: el when sin sujeto.
  • if (cond) a else b dentro de un println(…): el if como expresión.

Intenta escribirlo tú, en dos ficheros:

  • Puntos.kt: añade puntosPor(golesAFavor, golesEnContra), que devuelve los puntos de un partido: 3 si ganas, 1 si empatas y 0 si pierdes. Ponla encima de encabezadoDeJornada.
  • Main.kt: sustituye todo su contenido. El programa presenta el torneo («Copa Barrio»), escribe el encabezado de las jornadas 1, 2 y 3 de 3 (con argumentos con nombre) y uno más con el prefijo «Fecha», escribe los puntos del local para los marcadores 2-1, 1-1 y 0-3, y dice cuánto vale un empate.

Al ejecutar ./gradlew :torneo:run debe verse:

Torneo: Copa Barrio
Jornada 1 de 3
Jornada 2 de 3
Jornada 3 de 3
Fecha 1 de 3
2-1 -> 3 puntos para el local
1-1 -> 1 puntos para el local
0-3 -> 0 puntos para el local
Un empate vale 1 punto

El título de cada bloque de abajo es la ruta del fichero en tu proyecto. En cada uno, selecciona todo (Cmd + A) y pega el bloque.

Intenta primero. Cuando termines, compara con la solución.Mostrar el código
torneo/src/main/java/com/sebastiannarvaez/marcador/torneo/Puntos.kt
package com.sebastiannarvaez.marcador.torneo
 
// Función suelta: escrita directamente en el fichero.
// Función de expresión: el cuerpo es un único `when` que devuelve el valor.
fun puntosPor(golesAFavor: Int, golesEnContra: Int): Int = when {
    golesAFavor > golesEnContra -> 3
    golesAFavor == golesEnContra -> 1
    else -> 0
}
 
// Parámetros con valor por defecto: se pueden omitir al llamar.
fun encabezadoDeJornada(numero: Int, totalDeJornadas: Int, prefijo: String = "Jornada"): String =
    "$prefijo $numero de $totalDeJornadas"
torneo/src/main/java/com/sebastiannarvaez/marcador/torneo/Main.kt
package com.sebastiannarvaez.marcador.torneo
 
// Punto de entrada de consola: ./gradlew :torneo:run
fun main() {
    val nombreDelTorneo = "Copa Barrio"
    val totalDeJornadas = 3
    println("Torneo: $nombreDelTorneo")
 
    // Argumento con nombre: se lee sin adivinar qué es cada número.
    println(encabezadoDeJornada(numero = 1, totalDeJornadas = totalDeJornadas))
    println(encabezadoDeJornada(numero = 2, totalDeJornadas = totalDeJornadas))
    println(encabezadoDeJornada(numero = 3, totalDeJornadas = totalDeJornadas))
    println(encabezadoDeJornada(1, 3, prefijo = "Fecha"))
 
    // Los puntos del local para tres marcadores.
    println("2-1 -> ${puntosPor(2, 1)} puntos para el local")
    println("1-1 -> ${puntosPor(1, 1)} puntos para el local")
    println("0-3 -> ${puntosPor(0, 3)} puntos para el local")
 
    // `if` como expresión.
    val hayEmpates = puntosPor(1, 1) == 1
    println(if (hayEmpates) "Un empate vale 1 punto" else "Sin empates")
}

Lo que ves en Puntos.kt: puntosPor es una función suelta y de expresión, con un when sin sujeto, así que sus ramas son condiciones y necesita else.

Punto de control

  • En la vista Android, el paquete com.sebastiannarvaez.marcador.torneo de torneo → kotlin+java contiene Main.kt y Puntos.kt.
  • ./gradlew :torneo:run imprime las nueve líneas de arriba, de Torneo: Copa Barrio a Un empate vale 1 punto.
  • Puntos.kt tiene una función de expresión con when y una con valor por defecto. Todavía no hay tests: no hay nada que contar hasta la lección siguiente, donde aprenderás a abrir el informe de tests.

Comprueba lo que entendiste

4 preguntas · necesitas 3 para completar la lección

  1. 1.Una función puntosPor(a: Int, b: Int): Int usa un when sin sujeto con tres ramas: si a > b da 3, si a == b da 1 y en el resto de casos (else) da 0. ¿Qué devuelve con puntosPor(0, 2)?

  2. 2.¿Qué pasa si escribes val x = if (minuto > 45) 3 sin else?

  3. 3.Con val edad = 15 y val conPermiso = true, ¿qué da edad >= 18 || conPermiso?

  4. 4.Con var edad = 15, ¿qué diferencia hay entre edad = 20 y edad == 20?

Faltan 4 de 4