Control de flujo en Kotlin: Condicionales (if, when) y bucles (for, while)

Introducción

Hasta ahora, todos los programas que hemos escrito se ejecutaban en línea recta: el ordenador leía la línea 1, luego la 2, luego la 3… y terminaba. Pero en el mundo real, las aplicaciones necesitan tomar decisiones (si el usuario tiene saldo, haz la compra; si no, muestra un error) y repetir tareas (mostrar los 50 mensajes de un chat uno por uno).

Para esto sirve el Control de Flujo. En Kotlin, contamos con herramientas modernizadas y súper potentes para dirigir el tráfico de nuestro código. ¡Vamos a descubrirlas!

Tomando decisiones: El clásico if / else

La forma más básica de tomar una decisión en programación es usar la estructura if (si ocurre esto…) y else (si no, haz esto otro…).

La condición a evaluar siempre va entre paréntesis (), y el bloque de código que se ejecutará va entre llaves {}.

fun main() {
    val edad = 18

    if (edad >= 18) {
        println("Puedes entrar a la discoteca.")
    } else {
        println("Lo siento, vuelve a casa.")
    }
}

El súper-poder de Kotlin: El if como expresión

Si vienes de lenguajes como Java o JavaScript, conocerás el famoso operador ternario (condicion ? valor1 : valor2) para asignar variables en una sola línea. En Kotlin, el operador ternario no existe porque no hace falta.

En Kotlin, un if puede devolver un valor directamente. Si tu if y tu else solo tienen una línea, puedes quitar las llaves y hacerlo así de elegante:

fun main() { 
    val a = 10
    val b = 20

    // El resultado del if se guarda directamente en la variable 'mayor'
    val mayor = if (a > b) a else b 
    
    println("El número mayor es $mayor") // Imprime: 20
}

El condicional when: El switch con esteroides

Cuando tienes que evaluar muchísimas opciones distintas, usar decenas de if / else if / else encadenados hace que el código sea ilegible.

Otros lenguajes usan la palabra switch. Kotlin usa when (cuando), y es una de las herramientas más queridas por los desarrolladores.

when como instrucción

Colocamos la variable que queremos evaluar entre paréntesis. Luego usamos una «flechita» -> para indicar qué hacer en cada caso. El else actúa como la opción por defecto si no se cumple ninguna de las anteriores.

fun main() {
    val boton = "X"

    when (boton) {
        "A" -> println("Saltar")
        "B" -> println("Atacar")
        "X" -> println("Abrir Inventario")
        "Y" -> println("Magia")
        else -> println("Botón no reconocido") 
    }
}

Nota: Kotlin evalúa de arriba a abajo. En cuanto encuentra una coincidencia, ejecuta esa línea y sale del when automáticamente. (¡Adiós a la pesadilla de olvidar poner los break de otros lenguajes!)

when como expresión (Devolviendo un valor)

Al igual que el if, podemos usar when para asignar un valor directamente a una variable:

val estadoSemaforo = "Rojo" 
 
val accion = when (estadoSemaforo) {
    "Verde" -> "Acelerar"
    "Ambar" -> "Frenar poco a poco"
    "Rojo" -> "Detenerse"
    else -> "Llamar al mecánico" // Al devolver valor, el 'else' es OBLIGATORIO
}
println("Debes: $accion")

Rangos: preparando el terreno para los bucles

Antes de aprender a repetir tareas, necesitamos saber cómo crear Rangos (intervalos de valores) en Kotlin. Es facilísimo:

  • .. (Punto punto): Crea un rango que incluye el último número. 1..4 equivale a 1, 2, 3, 4.
  • ..< (Punto punto menor): Crea un rango que excluye el último número. 1..<4 equivale a 1, 2, 3.
  • downTo: Cuenta hacia atrás. 4 downTo 1 equivale a 4, 3, 2, 1.
  • step: Cambia el tamaño del salto. 1..5 step 2 equivale a 1, 3, 5.

(¡También funciona con letras del abecedario! Ej: 'a'..'d')

Bucles: repitiendo tareas sin cansarse

El bucle for (para cada…)

Se usa cuando sabes exactamente cuántas veces quieres repetir algo, o cuando quieres recorrer una Colección (como las Listas que vimos en el artículo anterior).

fun main() {
    // Repetir un código un número exacto de veces usando un rango
    for (numero in 1..5) { 
        print(numero) // Imprime: 12345
    }

    println() // Salto de línea

    // Recorrer una lista
    val pasteles = listOf("Zanahoria", "Queso", "Chocolate")
    for (pastel in pasteles) {
        println("¡Qué rico, un pastel de $pastel!")
    }
}

Los bucles while y do-while (mientras que…)

Se usan cuando no sabes cuántas veces se va a repetir algo, pero sabes que debe repetirse «mientras» se cumpla una condición.

  • while: Primero comprueba la condición. Si es falsa desde el principio, nunca se ejecuta.
  • do-while: Primero ejecuta el código una vez, y luego comprueba la condición. Te asegura que el bloque de código se va a ejecutar como mínimo una vez.
fun main() {
    var porcionesComidas = 0
    
    // Bucle while normal
    while (porcionesComidas < 3) {
        println("Me como una porción")
        porcionesComidas++ // Esto suma 1 a la variable (es lo mismo que porcionesComidas = porcionesComidas + 1)
    }
}

Ejercicios

Abre tu Kotlin Playground y vamos a machacar lo aprendido.

Los dados

Crea un minijuego donde ganas si al lanzar dos dados sacas el mismo número. Si son iguales, imprime "¡Has ganado :)". Si no, "Has perdido :(".

(Nota: Para generar números aleatorios usaremos una librería nativa de Kotlin llamada Random).

import kotlin.random.Random

fun main() {
    // Genera un número aleatorio entre 0 y 5.
    val dado1 = Random.nextInt(6) 
    val dado2 = Random.nextInt(6)
    
    println("Dado 1: $dado1 | Dado 2: $dado2")
    // Escribe tu código (if/else) a partir de aquí:
    
}

Botones de consola

Usando un when que actúe como expresión (asignando su resultado o metiéndolo directo en un println), haz que el programa imprima la acción correspondiente al botón pulsado:

  • A -> «Sí»
  • B -> «No»
  • X -> «Menú»
  • Y -> «Nada»
  • Cualquier otro -> «No existe ese botón»
fun main() {
    val boton = "A"
    
    // Escribe tu when aquí dentro del println
    println(
        // ...
    )
}

Comiendo Pizza

Tienes un código muy feo que cuenta porciones de pizza repitiendo líneas a mano. Conviértelo en un bucle while que cuente automáticamente hasta llegar a las 8 porciones.

fun main() {
    var porciones = 0
    // ¡Borra este desastre y usa un bucle while!
    porciones++
    println("Solo hay $porciones porción/es de pizza :(")
    porciones++
    println("Solo hay $porciones porción/es de pizza :(")
    // ... así hasta 7 ...
    
    // Al salir del bucle debe imprimir esto (y la variable debe valer 8):
    println("¡Tenemos $porciones porciones! ¡Una pizza entera! :D")
}

El clásico reto FizzBuzz

Este es uno de los ejercicios más famosos en las entrevistas de programación junior.

Escribe un programa que imprima los números del 1 al 100, pero:

  • Si el número es divisible por 3, imprime la palabra "fizz" en lugar del número.
  • Si el número es divisible por 5, imprime "buzz".
  • Si el número es divisible por 3 Y por 5 (es decir, divisible por 15), imprime "fizzbuzz".

Pista: Usa un bucle for del 1 al 100. Dentro, usa un when sin argumento para evaluar condiciones usando el operador Módulo % (que te da el resto de una división. Si numero % 3 == 0, es que es divisible por 3).

fun main() {
    // Escribe tu código aquí (for + when)
}

Soluciones a los ejercicios

Los dados

import kotlin.random.Random

fun main() {
    val dado1 = Random.nextInt(6)
    val dado2 = Random.nextInt(6)
    println("Dado 1: $dado1 | Dado 2: $dado2")
    
    // Comprobamos la igualdad con ==
    if (dado1 == dado2) {
        println("¡Has ganado :)")
    } else {
        println("Has perdido :(")
    }
}

Botones de consola

fun main() {
    val boton = "A"
    
    println(
        when (boton) {
            "A" -> "Sí"
            "B" -> "No"
            "X" -> "Menú"
            "Y" -> "Nada"
            else -> "No existe ese botón"
        }
    )
}

La pizza

fun main() {
    var porciones = 0
    
    while (porciones < 7) {
        porciones++
        println("Solo hay $porciones porción/es de pizza :(")
    }
    
    porciones++ // Sumamos la octava porción al salir
    println("¡Tenemos $porciones porciones! ¡Una pizza entera! :D")
}

El clásico reto FizzBuzz

fun main() {
    for (numero in 1..100) {
        println(
            // Al usar when SIN variable, podemos evaluar condiciones booleanas libres
            // ¡El orden importa! Hay que comprobar el 15 primero.
            when {
                numero % 15 == 0 -> "fizzbuzz"
                numero % 3 == 0 -> "fizz"
                numero % 5 == 0 -> "buzz"
                else -> numero // Si no cumple ninguna, imprime el número normal
            }
        )
    }
}

Guía Definitiva de colecciones en Kotlin: listas, conjuntos y mapas explicados desde cero

Introducción

En las unidades anteriores aprendimos a guardar un dato individual en una variable (como un número o un texto). Pero, ¿qué pasa si estás creando una app y necesitas guardar los nombres de 100 usuarios? ¿Vas a crear 100 variables distintas? ¡Por supuesto que no!

Para eso existen las colecciones. Son estructuras que nos permiten agrupar múltiples datos bajo un mismo nombre para procesarlos más tarde.

En Kotlin, la regla de seguridad que vimos con val y var se mantiene: las colecciones pueden ser de solo lectura (inmutables) o mutables (modificables). Kotlin nos ofrece tres tipos principales. ¡Vamos a destriparlas!

Listas (Lists): El cajón ordenado

Una Lista (List) es exactamente lo que imaginas: una colección de elementos ordenados uno detrás de otro.

  • Tienen un orden estricto (el primero, el segundo, el tercero…).
  • Permiten duplicados (puedes tener el mismo elemento varias veces).

Crear listas

Para crear una lista de solo lectura usamos listOf(). Si queremos una lista a la que podamos añadir o quitar cosas en el futuro, usamos mutableListOf().

fun main() { 
    // Lista de solo lectura (Kotlin deduce que es de tipo String)
    val formas = listOf("triángulo", "cuadrado", "círculo")
    println(formas) // [triángulo, cuadrado, círculo]
    
    // Lista mutable (Aquí le decimos explícitamente el tipo <String>)
    val formasMutables: MutableList<String> = mutableListOf("triángulo", "cuadrado", "círculo")
}

Profundizando: El índice cero y los errores comunes

Como las listas están ordenadas, cada elemento tiene una posición o «índice». ¡Ojo! En programación, siempre empezamos a contar desde el cero.

Piensa en los ascensores en España: cuando entras al edificio desde la calle, estás en la Planta Baja (índice 0). Si subes un piso, llegas a la Planta 1 (el índice 1, que en realidad es el segundo nivel). El índice te dice cuántos saltos das desde el principio.

fun main() { 
    val formas = listOf("triángulo", "cuadrado", "círculo")
    
    // Acceder por su posición (índice)
    println("El primer elemento es: ${formas[0]}") // triángulo
    
    // ¡CUIDADO! El terror de los novatos: IndexOutOfBoundsException
    // println(formas[3]) -> ¡El programa explotará porque no hay un 4º elemento!
}

Kotlin también nos regala funciones súper útiles para no tener que lidiar siempre con los números:

fun main() {
    val formas = listOf("triángulo", "cuadrado", "círculo")
    
    println("El primer elemento: ${formas.first()}") // triángulo
    println("El último elemento: ${formas.last()}") // círculo
    println("Total de elementos: ${formas.count()}") // 3
    
    // Comprobar si algo existe con la palabra 'in'
    println("círculo" in formas) // true
}

Modificar listas mutables y el «truco del candado»

Si tu lista es MutableList, usas .add() para añadir y .remove() para borrar.

val carrito: MutableList<String> = mutableListOf("Manzanas", "Pan")
carrito.add("Leche") // [Manzanas, Pan, Leche]
carrito.remove("Pan") // [Manzanas, Leche]

A veces tienes una lista mutable, pero se la vas a pasar a otra parte de tu código y no quieres que se modifique por accidente. Puedes «disfrazarla» de solo lectura:

val inventarioMutable: MutableList<String> = mutableListOf("Espada", "Poción")
val inventarioBloqueado: List<String> = inventarioMutable // ¡Candado puesto! Ahora no se puede modificar.

Conjuntos (Sets): El VIP de la exclusividad

Un conjunto (Set) es parecido a una lista, pero con dos diferencias vitales:

  • No tienen orden (no puedes pedir el elemento [0]).
  • NO permiten elementos duplicados. Son únicos y exclusivos.

Se crean con setOf() y mutableSetOf().

fun main() {
    // Fíjate que ponemos "cereza" dos veces
    val frutas = setOf("manzana", "plátano", "cereza", "cereza")
    
    // Al imprimir, ¡la segunda cereza ha desaparecido mágicamente!
    println(frutas) // [manzana, plátano, cereza]
    println("Hay ${frutas.count()} frutas únicas") // 3
}

Profundizando: ¿Por qué usar Sets? ¡Por la velocidad!

Si quieres saber si el email «[email protected]» está en una Lista de 1 millón de usuarios, el ordenador mirará uno a uno. Es lentísimo.

Si usas un Set, Kotlin usa matemáticas internas (una función Hash) para saber exactamente dónde está guardado. Lo encuentra al instante. ¡Usa Sets cuando trabajes con muchos datos únicos!

val millonesDeUsuarios = setOf("[email protected]", "[email protected]")
if ("[email protected]" in millonesDeUsuarios) {
    println("¡El usuario ya existe!") // Comprobación súper rápida
}

Mapas (Maps): El diccionario de datos

Un Mapa (Map) almacena datos en pares Clave-Valor (Key-Value).

Piénsalo como la carta de un restaurante: el nombre del plato es la clave y su precio es el valor.

  • Las claves (keys) deben ser únicas.
  • Los valores (values) sí pueden repetirse.

Se crean con mapOf() y mutableMapOf(). Usamos la palabrita to para unir la pareja.

fun main() {
    // Especificamos explícitamente: Claves String, Valores Int
    val menuZumos: MutableMap<String, Int> = mutableMapOf("manzana" to 3, "kiwi" to 4)
    
    // Leer un valor usando su clave entre corchetes
    println("El zumo de manzana cuesta: ${menuZumos["manzana"]}€") // 3€
}

Profundizando: Nulos y sobreescritura

¿Qué pasa si buscas algo que no existe en el menú? Kotlin te devuelve null (nulo, vacío). Así evita que tu aplicación se cuelgue por un error.

println(menuZumos["piña"]) // Imprime: null

¿Y si añado una clave que ya existe? Se sobreescribe el valor antiguo. Es la forma oficial de actualizar datos:

menuZumos["manzana"] = 5 // ¡Inflación! Actualizamos el precio a 5€
menuZumos["coco"] = 6    // Como "coco" no existe, lo añade nuevo al mapa
menuZumos.remove("kiwi") // Eliminamos el kiwi

println(menuZumos.keys)   // [manzana, coco]
println(menuZumos.values) // [5, 6]

Ejercicios

Abre el Kotlin Playground e intenta resolver estos ejercicios para asentar lo aprendido.

Sumando listas

Tienes una lista de números «verdes» y otra de «rojos». Imprime cuántos números hay en total sumando el tamaño de ambas listas.

fun main() {
    val numerosVerdes = listOf(1, 4, 23)
    val numerosRojos = listOf(17, 2)
    // Escribe tu código aquí:
}

El protocolo de red

Tienes un Set con protocolos en mayúsculas. Un usuario pide «smtp» en minúsculas. Comprueba si está soportado (debe devolver un Boolean).

Pista: Usa .uppercase() en la petición para pasarla a mayúsculas antes de buscar en el Set con in.

fun main() {
    val SOPORTADOS = setOf("HTTP", "HTTPS", "FTP")
    val peticion = "smtp"
    val estaSoportado = // Escribe tu código aquí 
    println("Soporte para $peticion: $estaSoportado")
}

Diccionario de números

Crea un Map que relacione los números del 1 al 3 con su nombre escrito («uno», «dos», «tres»). Imprime cómo se escribe el número 2 buscándolo en el mapa.

fun main() {
    val numeroAPalabra = // Escribe tu código aquí
    val n = 2
    // Imprime el resultado
}

El inventario del héroe

Crea una lista mutable llamada inventario con: «Espada», «Escudo», «Poción».

Añade un «Arco». Elimina la «Poción». Finalmente, imprime el inventario y cuántos objetos tiene usando .count().

Soluciones a los ejercicios

¡No mires hasta haberlo intentado!

Sumando listas

fun main() {
    val numerosVerdes = listOf(1, 4, 23)
    val numerosRojos = listOf(17, 2)
    val total = numerosVerdes.count() + numerosRojos.count()
    println("Total: $total números")
}

El protocolo de red

fun main() {
    val SOPORTADOS = setOf("HTTP", "HTTPS", "FTP")
    val peticion = "smtp"
    val estaSoportado = peticion.uppercase() in SOPORTADOS 
    println("Soporte para $peticion: $estaSoportado") // false
}

Diccionario de números

fun main() {
    val numeroAPalabra = mapOf(1 to "uno", 2 to "dos", 3 to "tres")
    val n = 2
    println("El número $n se escribe como '${numeroAPalabra[n]}'")
}

El inventario del héroe

fun main() {
    val inventario = mutableListOf("Espada", "Escudo", "Poción")
    inventario.add("Arco")
    inventario.remove("Poción")
    println("Inventario: $inventario")
    println("Tienes ${inventario.count()} objetos.")
}

Tipos básicos en Kotlin: Inferencia, números, textos y operaciones

Introducción

En la unidad anterior vimos cómo hacer nuestro primer «Hola Mundo» y cómo usar variables (val y var). Pero nos dejamos un detalle importantísimo en el tintero: ¿qué tipo de información estamos guardando exactamente en esas variables?

En Kotlin, absolutamente todo tiene un tipo. Los tipos son fundamentales porque le dicen al compilador (el «cerebro» que lee tu código) qué puedes y qué no puedes hacer con una variable. Por ejemplo, puedes multiplicar dos números, pero no puedes multiplicar dos palabras, ¿verdad?

Ahora vamos a desgranar los tipos básicos de Kotlin, a aprender a operar con ellos y a descubrir cómo Kotlin nos facilita la vida con su «magia» deductiva.

La inferencia de tipos: Kotlin es muy listo

Si recuerdas la unidad anterior, declarábamos variables así:

var clientes = 10

En ningún momento le dijimos a Kotlin: «Oye, que sepas que clientes es un número entero». Sin embargo, Kotlin lo supo al instante. Esta capacidad se llama Inferencia de Tipos (Type Inference).

Como a la variable clientes le asignamos un 10, Kotlin deduce inmediatamente que su tipo es numérico, concretamente un Int (entero). Gracias a esto, el compilador sabe que puedes realizar operaciones matemáticas con esta variable.

Mira este ejemplo de asignaciones compuestas (una forma abreviada de hacer matemáticas):

fun main() {
    var clientes = 10

    // Vienen 3 clientes más (clientes = clientes + 3)
    clientes += 3  // Ahora hay 13
    
    // Se van 5 clientes
    clientes -= 5  // Ahora hay 8
    
    // El negocio explota y multiplicamos los clientes por 2
    clientes *= 2  // Ahora hay 16
    
    // Dividimos a los clientes en 4 grupos
    clientes /= 4  // Ahora hay 4 en cada grupo

    println(clientes) 
    // Resultado: 4
}

Los tipos básicos de Kotlin: La lista completa

Aunque Kotlin infiere los tipos, a veces querrás (o necesitarás) ser explícito. Para declarar un tipo manualmente, se usan los dos puntos : después del nombre de la variable.

A continuación mostramos los tipos fundamentales.

Números enteros (sin decimales)

  • Int: Es el estándar para números sin decimales. (Ej: val año: Int = 2024)
  • Long: Se usa para números ridículamente grandes. Si un número es demasiado grande para un Int, Kotlin lo convertirá en Long automáticamente. También puedes forzarlo añadiendo una L mayúscula al final. (Ej: val estrellas: Long = 9876543210L)
  • Byte y Short: Se usan en casos muy específicos para ahorrar memoria con números pequeños. Raramente los usarás al empezar.
  • Nota: Kotlin también tiene versiones «Unsigned» (sin signo, es decir, solo positivos) como UInt o ULong marcados con una u (Ej: val puntos: UInt = 100u).

Números con decimales (coma flotante)

  • Double: Es el estándar de Kotlin para números con decimales. Tiene una precisión doble, es decir, admite muchísimos decimales. (Ej: val precio: Double = 19.99)
  • Float: Ocupa menos memoria pero es menos preciso. Para indicarle a Kotlin que quieres un Float y no un Double, debes añadir una f o F al final del número. (Ej: val temperatura: Float = 24.5f)

Booleanos (verdadero o falso)

  • Boolean: Solo puede tener dos valores: true (verdadero) o false (falso). Es la base de la lógica en programación (Ej: val estaEncendido: Boolean = true).

Caracteres y cadenas de texto

  • Char: Representa un único carácter (una sola letra, número o símbolo). Se escribe entre comillas simples ' '. (Ej: val inicial: Char = 'J')
  • String: Representa una cadena de texto (muchos caracteres juntos). Se escribe entre comillas dobles " ". (Ej: val mensaje: String = "¡Hola, mundo!")

Declarar ahora, inicializar después

A veces sabes qué tipo de dato vas a guardar, pero aún no tienes el valor exacto. Kotlin te permite declarar una variable y asignarle su valor más tarde.

Eso sí, en estos casos es obligatorio especificar el tipo explícitamente, porque Kotlin no tiene un valor inicial del que deducirlo:

fun main() {
    // Declaramos la variable especificando el tipo explícitamente, pero sin darle valor
    val d: Int 
    
    // Más adelante en el código, la inicializamos
    d = 3 

    println(d) // Imprime: 3
}

La seguridad de Kotlin frente a errores

¿Qué pasa si intentas imprimir la variable d antes de darle un valor? En otros lenguajes tu programa explotaría (el temido NullPointerException o imprimiría basura de la memoria). Kotlin no te deja hacerlo. El código directamente se pondrá en rojo y no compilará, mostrándote el error: «Variable ‘d’ must be initialized». ¡Un salvavidas enorme!

Ejercicios

Abre tu editor o el Kotlin Playground y pon a prueba lo que acabas de aprender.

El tipado explícito

Kotlin puede inferir los tipos, pero en este ejercicio queremos que seas tú quien los escriba. Modifica el siguiente código para añadir explícitamente (: Tipo) el tipo de dato correcto a cada variable:

fun main() {
    val a = 1000 
    val b = "mensaje de registro"
    val c = 3.14
    val d = 100_000_000_000_000 // Fíjate, los guiones bajos sirven para leer mejor los números grandes
    val e = false
    val f = '\n' // Esto es un carácter especial que representa un "salto de línea"
}

El cofre del tesoro

Crea un programa que simule el oro de un jugador en un videojuego usando una variable mutable (var) llamada monedas:

  1. El jugador empieza con 50 monedas.
  2. Encuentra un cofre mágico y su oro se multiplica por 3. (Usa asignación compuesta *=).
  3. Compra una espada que cuesta 80 monedas. (Usa -=).
  4. Imprime el resultado final: "Tras la aventura, te quedan X monedas".

Detecta el error

El siguiente código tiene un problema y Kotlin se quejará si intentas ejecutarlo. Corrígelo para que funcione y se imprima la edad correctamente.

fun main() {
    val edadUsuario: Int
    println("La edad del usuario es $edadUsuario")
    edadUsuario = 25
}

Cuidado con las divisiones

Si divides dos variables Int (ej: 10 / 3), Kotlin devuelve un número entero (3) y se come los decimales.

Crea dos variables llamadas dividendo (valor 10) y divisor (valor 3). Haz que sean de tipo Double explícitamente para que, al imprimirlas usando una plantilla de cadena, el resultado sea con decimales (3.3333333333333335).

Soluciones a los ejercicios

¡No mires hasta que no te hayas peleado un rato con el código!

El tipado explícito

fun main() {
    val a: Int = 1000 
    val b: String = "mensaje de registro"
    val c: Double = 3.14  // Al tener decimales y no tener 'f', es Double
    val d: Long = 100_000_000_000_000 // Es demasiado grande para ser Int
    val e: Boolean = false // Verdadero o falso
    val f: Char = '\n' // Comillas simples indican que es un solo Char
}

El cofre del tesoro

fun main() {
    var monedas = 50
    monedas *= 3
    monedas -= 80
    println("Tras la aventura, te quedan $monedas monedas") 
    // Imprimirá 70
}

Detecta el error

fun main() {
    val edadUsuario: Int
    edadUsuario = 25 // ¡Había que inicializarla ANTES de leerla!
    println("La edad del usuario es $edadUsuario")
}

Cuidado con las divisiones

fun main() {
    // Para forzar que un número entero se trate como decimal, ponemos .0
    val dividendo: Double = 10.0 
    val divisor: Double = 3.0
    
    println("El resultado exacto es ${dividendo / divisor}")
}

Primeros pasos con Kotlin: Tu primer «Hola mundo», variables y plantillas de texto

Introducción

Si estás empezando en el mundo del desarrollo (ya sea para Android o backend), Kotlin es uno de los lenguajes más modernos, limpios y seguros que puedes aprender hoy en día.

En esta unidad, vamos a dar los primeros pasos. Entenderemos cómo funciona la estructura básica de un programa, cómo almacenar información usando variables y cómo imprimir mensajes por pantalla de forma elegante. Al final, tendrás ejercicios prácticos para asentar lo aprendido.

El clásico «Hola mundo»

Por tradición, el primer programa que todo desarrollador escribe al aprender un lenguaje nuevo es el que imprime las palabras «¡Hola, mundo!» en la pantalla.

En Kotlin, se hace así de fácil:

fun main() {
    println("¡Hola, mundo!")
    // ¡Hola, mundo!
}

Analizando el código línea a línea:

  • fun: Es la palabra reservada (abreviatura de function) que usamos en Kotlin para declarar una función. Una función no es más que un bloque de código que realiza una tarea específica.
  • main(): No es una función cualquiera. Es el punto de entrada (entry point) de tu programa. Siempre que ejecutes una aplicación en Kotlin, el ordenador buscará esta función y empezará a ejecutar las instrucciones que haya dentro de ella.
  • { } (Las llaves): Todo lo que esté dentro de las llaves es el «cuerpo» de la función, es decir, las instrucciones que se van a ejecutar.
  • println(): Es una función nativa de Kotlin que coge lo que le pongas entre paréntesis (los «argumentos») y lo imprime por la salida estándar (la consola de tu pantalla). Al terminar de imprimir, añade un salto de línea (por eso termina en ln, de line). Si usas print() (sin el ln), el texto se imprimirá, pero el siguiente mensaje aparecerá pegado justo a la derecha, en la misma línea.

El dato: ¿Te has dado cuenta de algo? ¡No hay punto y coma (;) al final de la línea! En lenguajes más antiguos como Java o C++, olvidar el punto y coma era un dolor de cabeza. Kotlin es un lenguaje moderno y no los necesita.

Variables: Guardando información en memoria

Cualquier programa informático necesita almacenar datos (nombres de usuario, puntuaciones de un juego, precios…). Para eso utilizamos las variables.

En Kotlin, tenemos dos formas principales de crear variables, y esto es muy importante:

  1. Variables de solo lectura (inmutables) con val (de value).
  2. Variables mutables con var (de variable).

Para asignarle un valor a una variable, simplemente usamos el operador de asignación =. Fíjate en este ejemplo:

fun main() { 
    val palomitas = 5   // Tenemos 5 cajas de palomitas
    val perritos = 7    // Tenemos 7 perritos calientes
    var clientes = 10   // Hay 10 clientes en la cola
    
    // De repente, un par de clientes se cansan de esperar y se van de la cola
    clientes = 8
    println(clientes) 
    // Resultado en pantalla: 8
}

val vs var: ¿Cuál debería usar?

Como clientes se declaró con var, pudimos reasignarle un nuevo valor (8) más adelante en el programa. Sin embargo, si intentáramos hacer palomitas = 6, el compilador de Kotlin nos daría un error, porque palomitas es un val y no puede cambiar una vez se le ha dado un valor inicial.

La regla de oro en Kotlin: Utiliza siempre val por defecto. Usa var única y exclusivamente cuando sepas seguro que ese valor va a tener que cambiar en el futuro (como un contador, o la vida de un personaje en un juego). Esto hace que tu código sea mucho más seguro, predecible y libre de errores accidentales (los temidos bugs).

Plantillas de cadenas (String Templates): Texto inteligente

En programación, es súper común querer imprimir un texto que contenga el valor de nuestras variables. En otros lenguajes tendrías que «sumar» o concatenar trozos de texto con el símbolo +, lo cual queda feo y es fácil equivocarse.

Kotlin lo soluciona de forma magistral con las String Templates (plantillas de cadenas).

Una cadena de texto (String) se escribe siempre entre comillas dobles " ". Para inyectar el valor de una variable dentro de ese texto, solo tienes que poner el símbolo del dólar $ seguido del nombre de la variable.

Y si lo que quieres es hacer una operación matemática u otro código más complejo dentro del texto, lo metes entre llaves ${ }. Mira por ejemplo el siguiente código:

fun main() { 
    val clientes = 10
    
    // Imprimiendo una variable directamente
    println("Actualmente hay $clientes clientes esperando.")
    // Salida: Actualmente hay 10 clientes esperando.
    
    // Haciendo una operación matemática dentro del texto
    println("Si llega uno más, habrá ${clientes + 1} clientes en total.")
    // Salida: Si llega uno más, habrá 11 clientes en total.
}

La magia oculta: La inferencia de tipos

Si prestas atención, en ningún momento le hemos dicho a Kotlin que clientes es un número. Kotlin es muy listo: al ver que le asignamos un 10, él automáticamente infiere (deduce) que el tipo de dato es un número entero (Int).

Ejercicios

La programación solo se aprende tecleando. Aquí tienes varios ejercicios de menos a más dificultad. Intenta resolverlos por tu cuenta en un editor o en el Kotlin Playground antes de mirar las soluciones más abajo.

Presentando a Mary

Completa el siguiente código para que el programa imprima por consola el mensaje exacto: "Mary tiene 20 años". Debes usar obligatoriamente las variables dadas y las plantillas de cadenas ($).

fun main() {
    val nombre = "Mary"
    val edad = 20
    // Escribe tu código debajo de esta línea:
    
}

Batería del móvil

Crea un pequeño programa que simule la batería de tu móvil.

  1. Declara una variable llamada bateria que empiece al 100%. (Piensa si debe ser val o var).
  2. Imprime el texto: "La batería inicial es del 100%".
  3. Simula que juegas a un videojuego y la batería baja a 75. Actualiza el valor de la variable.
  4. Vuelve a imprimir el texto: "Tras jugar, la batería es del 75%".

Calculadora de la compra inteligente

Declara dos variables inmutables: precioZapatillas con un valor de 50, y cantidadCajas con un valor de 2.

Utilizando un único println() y las llaves de expresión ${}, imprime el siguiente mensaje calculando el total sobre la marcha:

"Has comprado 2 pares de zapatillas. El precio total es de 100 euros."

¿print o println?

Imagina que quieres que la consola muestre exactamente esto en dos líneas:

Hola Mundo
¡Amo Kotlin!

Escribe el código usando tres instrucciones de impresión para conseguir esa salida exacta. Haz que la palabra «Hola» y «Mundo» se impriman en instrucciones separadas pero se queden en la misma línea.

Soluciones a los ejercicios

¡No hagas trampas! Solo mira esto si ya has intentado resolverlos.

Presentando a Mary

fun main() {
    val nombre = "Mary"
    val edad = 20
    println("$nombre tiene $edad años")
}

Batería del móvil

fun main() {
    // Usamos 'var' porque la batería va a cambiar a lo largo del tiempo
    var bateria = 100
    println("La batería inicial es del $bateria%")
    
    bateria = 75
    println("Tras jugar, la batería es del $bateria%")
}

Calculadora de la compra inteligente

fun main() {
    val precioZapatillas = 50
    val cantidadCajas = 2
    
    // Usamos ${} para multiplicar variables dentro del mismo texto
    println("Has comprado $cantidadCajas pares de zapatillas. El precio total es de ${precioZapatillas * cantidadCajas} euros.")
}

¿print o println?

fun main() {
    print("Hola ")       // Usa print, por lo que la siguiente palabra se pega a esta. (Fíjate en el espacio al final)
    println("Mundo")     // Se pega al 'Hola ', y luego hace un salto de línea por el 'ln'
    println("¡Amo Kotlin!") // Se imprime en la nueva línea
}