Programación · n.º 15 entre los lenguajes más usados

Aprende Kotlin desde cero

El lenguaje recomendado para Android, moderno y seguro frente a los null.

  • 10 lecciones
  • 10 ejercicios interactivos
  • 19 conceptos clave
  • 10,8 % lo usa*

Qué es Kotlin y para qué sirve

Kotlin es un lenguaje de JetBrains que funciona sobre la máquina virtual de Java y es totalmente compatible con su código. Google lo recomienda para crear aplicaciones Android, y también se usa en servidores y para compartir código entre plataformas. Es más conciso que Java y evita por diseño muchos errores con valores nulos.

  • Aplicaciones Android
  • Servidores en la JVM
  • Código compartido entre Android, iOS y web

Para empezar: Prueba los ejemplos en el Kotlin Playground oficial, en el navegador, o instala IntelliJ IDEA o Android Studio.

Nivel 1 Básico

Lección 1 · Básico

Tu primer programa

Un programa de Kotlin empieza en la función main. println() escribe una línea; no hace falta punto y coma al final.

El compilador genera bytecode para la JVM (o código para Android, iOS o JavaScript, según el destino).

Ejemplo práctico
fun main() {
    println("¡Hola, mundo!")
    println(2 + 3) // 5
}

Ejercicio

Completa la palabra que declara una función.

 main() {
    println("Hola")
}
Ver la solución

Respuesta: fun. Las funciones de Kotlin se declaran con fun.

Lección 2 · Básico

val, var y plantillas de texto

val declara un valor que no se puede reasignar y var una variable que sí. Usa val siempre que puedas. El tipo se deduce, aunque puedes escribirlo: val edad: Int = 30.

Dentro de un texto, $nombre o ${expresión} insertan valores.

Ejemplo práctico
fun main() {
    val nombre = "Ana"
    var edad = 30
    edad += 1
    val precio: Double = 19.99

    println("$nombre tiene $edad años")
    println("Total: ${"%.2f".format(precio * 3)}")
    // nombre = "Eva"  // error: val cannot be reassigned
}

Ejercicio

¿Qué palabra declara un valor que no se puede reasignar?

Ver la solución

Respuesta: val. val es de solo asignación única; var permite cambiar el valor.

Lección 3 · Básico

if y when

En Kotlin, if es una expresión: devuelve un valor, así que no hace falta el operador ternario. when sustituye al switch y admite rangos (in 5..8) y condiciones.

Como when también devuelve un valor, el código queda corto y claro.

Ejemplo práctico
fun main() {
    val nota = 7
    val resultado = when (nota) {
        in 9..10 -> "Sobresaliente"
        in 5..8 -> "Aprobado"
        else -> "Suspenso"
    }
    println(resultado) // Aprobado

    val acceso = if (nota >= 5) "permitido" else "denegado"
    println(acceso)
}

Ejercicio

¿Qué sustituye en Kotlin al switch de otros lenguajes?

Ver la solución

Respuesta: when. when elige una rama según el valor o la condición, y devuelve un resultado.

Lección 4 · Básico

Colecciones y bucles

listOf() crea una lista de solo lectura y mutableListOf() una que puedes modificar. mapOf("a" to 1) crea un mapa.

for (x in lista) recorre los elementos y for (i in 1..3) un rango.

Ejemplo práctico
fun main() {
    val compra = mutableListOf("pan", "leche")
    compra.add("huevos")
    for (producto in compra) println("- $producto")

    val stock = mapOf("teclado" to 10, "raton" to 0)
    println(stock["teclado"])     // 10
    println(stock.size)           // 2
}

Ejercicio

¿Qué función crea una lista a la que puedes añadir elementos?

Ver la solución

Respuesta: mutableListOf(). listOf() es de solo lectura; mutableListOf() permite add, remove, etc.

Nivel 2 Intermedio

Lección 5 · Intermedio

Funciones

Las funciones se declaran con fun, los parámetros llevan su tipo y el tipo devuelto va tras los dos puntos. Si la función es una sola expresión, se escribe con =.

Los parámetros pueden tener valor por defecto y pasarse por nombre, lo que evita muchas sobrecargas.

Ejemplo práctico
fun precioFinal(precio: Double, iva: Double = 21.0): Double =
    precio * (1 + iva / 100)

fun main() {
    println(precioFinal(100.0))              // 121.0
    println(precioFinal(100.0, iva = 10.0))  // 110.00000000000001
}

Ejercicio

¿Por qué el segundo resultado sale 110.00000000000001?

Ver la solución

Respuesta: Porque Double es binario y no representa exactamente 0,1. Los Double siguen el estándar binario IEEE 754: 1,1 no tiene representación exacta. Para dinero se usa BigDecimal o se redondea al mostrar.

Lección 6 · Intermedio

Seguridad frente a null

Un String nunca puede ser null; si un valor puede faltar, se declara String?. El compilador no te deja usarlo sin comprobarlo antes.

?. llama a algo solo si no es null, ?: (el operador Elvis) da un valor por defecto y !! fuerza el uso y lanza un error si es null (mejor evitarlo).

Ejemplo práctico
fun main() {
    val apodo: String? = null
    println(apodo?.uppercase() ?: "Sin apodo")   // Sin apodo

    val nombre: String = "Ana"
    println(nombre.length)                        // 3

    // println(apodo.length)  // error de compilación: apodo puede ser null, hay que usar ?. o !!
}

Ejercicio

Completa el operador que da un valor por defecto si es null.

val visible = apodo  "Anónimo"
Ver la solución

Respuesta: ?:. ?: (Elvis) devuelve la parte derecha cuando la izquierda es null.

Lección 7 · Intermedio

Clases y data class

Las propiedades se declaran directamente en el constructor principal de la clase. Por defecto las clases no se pueden heredar; para permitirlo se marcan con open.

Una data class genera automáticamente toString, equals, hashCode y copy, ideal para clases que solo guardan datos.

Ejemplo práctico
class Cuenta(val titular: String) {
    var saldo = 0.0
        private set

    fun ingresar(cantidad: Double) {
        require(cantidad > 0) { "La cantidad debe ser positiva" }
        saldo += cantidad
    }
}

data class Producto(val nombre: String, val precio: Double)

fun main() {
    val c = Cuenta("Ana")
    c.ingresar(50.0)
    println(c.saldo)                          // 50.0

    val raton = Producto("Ratón", 12.5)
    println(raton.copy(precio = 9.99))        // Producto(nombre=Ratón, precio=9.99)
}

Ejercicio

¿Qué genera automáticamente una data class?

Ver la solución

Respuesta: toString, equals, hashCode y copy. Kotlin escribe por ti esos métodos a partir de las propiedades del constructor.

Nivel 3 Avanzado

Lección 8 · Avanzado

Lambdas y funciones de colección

Una lambda es una función escrita entre llaves que se pasa como dato. Si tiene un solo parámetro, puedes referirte a él como it.

Las colecciones traen funciones como filter, map, sortedBy o sumOf, que se encadenan para transformar datos de forma legible.

Ejemplo práctico
data class Producto(val nombre: String, val precio: Double)

fun main() {
    val productos = listOf(
        Producto("Teclado", 25.9),
        Producto("Ratón", 12.5),
        Producto("Cable", 4.99),
    )
    val caros = productos.filter { it.precio > 10 }.map { it.nombre }.sorted()
    println(caros)                                         // [Ratón, Teclado]
    println("%.2f".format(productos.sumOf { it.precio }))  // 43,39 o 43.39 según el idioma del sistema
}

Ejercicio

En productos.filter { it.precio > 10 }, ¿qué es it?

Ver la solución

Respuesta: Cada producto, por turno. it es el nombre implícito del único parámetro de la lambda: aquí, cada producto.

Lección 9 · Avanzado

Interfaces, herencia y clases selladas

Una interfaz declara funciones que las clases implementan con : y override. Para heredar de una clase, esta tiene que estar marcada como open o abstract.

Una clase o interfaz sealed solo admite los subtipos declarados en su mismo paquete y módulo, de modo que when sabe cuáles son todos y no necesita else.

Ejemplo práctico
sealed interface Resultado
data class Exito(val datos: String) : Resultado
data class Fallo(val mensaje: String) : Resultado

fun describir(r: Resultado): String = when (r) {
    is Exito -> "Recibido: ${r.datos}"
    is Fallo -> "Error: ${r.mensaje}"
}

fun main() {
    println(describir(Exito("3 pedidos")))
    println(describir(Fallo("sin conexión")))
}

Ejercicio

¿Por qué el when del ejemplo no necesita else?

Ver la solución

Respuesta: Porque la interfaz sellada tiene un conjunto cerrado de subtipos. Con tipos sellados, el compilador conoce todas las variantes y comprueba que las cubres.

Lección 10 · Avanzado

Corrutinas

Las corrutinas permiten escribir código asíncrono como si fuera secuencial. Una función suspend puede pausarse (por ejemplo, mientras espera la red) sin bloquear el hilo.

Se usan con la biblioteca oficial kotlinx.coroutines: launch inicia una tarea y async una que devuelve un resultado con await(). En Android son la forma recomendada de hacer trabajo en segundo plano.

Ejemplo práctico
// Requiere la dependencia org.jetbrains.kotlinx:kotlinx-coroutines-core
import kotlinx.coroutines.*

suspend fun descargar(nombre: String): String {
    delay(500)            // simula esperar a la red sin bloquear
    return "datos de $nombre"
}

fun main() = runBlocking {
    val a = async { descargar("pedidos") }
    val b = async { descargar("clientes") }
    println(a.await())
    println(b.await())    // las dos descargas han ido a la vez: unos 0,5 s en total
}

Ejercicio

Completa la palabra que marca una función que puede pausarse.

 fun cargar(): String { … }
Ver la solución

Respuesta: suspend. Las funciones suspend solo pueden llamarse desde corrutinas u otras funciones suspend.

¿Has terminado? Sigue practicando

Repasa los conceptos en el diccionario, practica con estas herramientas o prueba otro lenguaje: las ideas que has aprendido (variables, condiciones, funciones, objetos…) te servirán en cualquiera.

  • Imágenes y archivos

    Generador de códigos QR

    Crea códigos QR de enlaces, textos, contactos, correos, teléfonos y SMS que no caducan.

  • Texto y programación

    Base64, URL y JSON

    Codifica y decodifica Base64 y URL, y valida o formatea JSON con la línea del error.

  • Diagnóstico de periféricos

    Test de pantalla táctil

    Busca zonas de la pantalla que no responden y comprueba cuántos dedos detecta a la vez.

Fuentes consultadas

* Porcentaje de personas que dijeron haber usado Kotlin en la Stack Overflow Developer Survey 2026: Technology (pregunta sobre lenguajes de programación, de scripts y de marcado; todas las respuestas). El progreso de las lecciones se guarda solo en este navegador.