if como expresión
En Kotlin if no es solo un statement — es una expresión que retorna un valor. Esto elimina la necesidad del operador ternario ? : de Java:
// if como statement (igual que Java)
if (edad >= 18) {
println("Mayor de edad")
} else {
println("Menor de edad")
}
// if como expresión — retorna un valor
val mensaje = if (edad >= 18) "Mayor de edad" else "Menor de edad"
// Con bloques — el valor retornado es la última expresión del bloque
val categoria = if (puntos > 1000) {
println("Calculando categoría premium...")
"Premium"
} else if (puntos > 500) {
"Gold"
} else {
"Standard"
}
// Reemplaza el ternario de Java:
// Java: String msg = edad >= 18 ? "Mayor" : "Menor";
// Kotlin: val msg = if (edad >= 18) "Mayor" else "Menor"
when — el switch mejorado
when reemplaza al switch de Java pero es mucho más poderoso. También es una expresión:
// when básico — como switch
val dia = 3
val nombreDia = when (dia) {
1 -> "Lunes"
2 -> "Martes"
3 -> "Miércoles"
4 -> "Jueves"
5 -> "Viernes"
6, 7 -> "Fin de semana" // múltiples valores
else -> "Día inválido"
}
// when con rangos y condiciones
val descripcion = when (edad) {
in 0..12 -> "Niño"
in 13..17 -> "Adolescente"
in 18..64 -> "Adulto"
else -> "Adulto mayor"
}
// when sin argumento — reemplaza cadenas de if-else
val resultado = when {
temperatura < 0 -> "Bajo cero"
temperatura < 20 -> "Frío"
temperatura < 30 -> "Agradable"
else -> "Calor"
}
// when con type checking — muy poderoso con sealed classes
sealed class Forma
data class Circulo(val radio: Double) : Forma()
data class Rectangulo(val ancho: Double, val alto: Double) : Forma()
data class Triangulo(val base: Double, val altura: Double) : Forma()
fun area(forma: Forma): Double = when (forma) {
is Circulo -> Math.PI * forma.radio * forma.radio
is Rectangulo -> forma.ancho * forma.alto
is Triangulo -> forma.base * forma.altura / 2
// No necesita else — el compilador sabe que cubrís todos los casos
}
when exhaustivoCuando usás when como expresión con una sealed class, el compilador exige que cubras todos los casos. Si agregás un nuevo subtipo y olvidás manejarlo, el código no compila. Es la misma ventaja que mencionamos en la lección de StateFlow.
for y rangos
// Iterar sobre una colección
val nombres = listOf("Ana", "Carlos", "María")
for (nombre in nombres) {
println(nombre)
}
// Con índice
for ((indice, nombre) in nombres.withIndex()) {
println("$indice: $nombre")
}
// Rangos numéricos
for (i in 1..5) print("$i ") // 1 2 3 4 5
for (i in 1 until 5) print("$i ") // 1 2 3 4 (sin incluir 5)
for (i in 5 downTo 1) print("$i ")// 5 4 3 2 1
for (i in 0..10 step 2) print("$i ") // 0 2 4 6 8 10
// Iterar sobre un Map
val precios = mapOf("Tablet" to 299.0, "Phone" to 499.0)
for ((producto, precio) in precios) {
println("$producto: $$precio")
}
// repeat — cuando solo necesitás repetir N veces
repeat(3) { println("Hola!") }
// forEach — estilo funcional
nombres.forEach { println(it) }
nombres.forEachIndexed { i, nombre -> println("$i: $nombre") }
while y do-while
// while — igual que en Java
var intentos = 0
while (intentos < 3) {
val resultado = intentarConexion()
if (resultado.exitoso) break
intentos++
}
// do-while — ejecuta al menos una vez
do {
val input = leerInput()
procesarInput(input)
} while (input != "salir")
break y continue con labels
Kotlin permite etiquetar loops para controlar exactamente cuál romper o saltear en loops anidados:
// break en loop anidado — solo sale del loop interno
for (i in 1..3) {
for (j in 1..3) {
if (j == 2) break // solo sale del for(j)
println("$i,$j")
}
}
// Con label — sale del loop externo
externo@ for (i in 1..3) {
for (j in 1..3) {
if (i == 2 && j == 2) break@externo // sale del for(i)
println("$i,$j")
}
}
// continue con label — salta al siguiente del loop etiquetado
externo@ for (i in 1..3) {
for (j in 1..3) {
if (j == 2) continue@externo // salta al siguiente i
println("$i,$j")
}
}
Smart casts
Después de un chequeo de tipo o null, Kotlin sabe automáticamente el tipo exacto — no necesitás castear manualmente:
fun procesar(valor: Any) {
// is verifica el tipo
if (valor is String) {
// Acá el compilador sabe que valor es String — smart cast automático
println(valor.uppercase()) // no necesitás (valor as String).uppercase()
println(valor.length)
}
if (valor is Int) {
println(valor * 2) // Int smart cast
}
}
// Smart cast con null check
fun mostrar(texto: String?) {
if (texto == null) return
// Acá el compilador sabe que texto es String (no nullable)
println(texto.uppercase()) // sin ?. ni !!
}
// Smart cast en when
fun describir(obj: Any): String = when (obj) {
is Int -> "Entero: ${obj * 2}" // obj es Int acá
is String -> "Texto: ${obj.length} chars" // obj es String acá
is List<*> -> "Lista: ${obj.size} elementos"
else -> "Otro: $obj"
}