La mejor manera de entender un lenguaje es escribir código en él. No leer sobre él, no ver webinars, no guardar artículos para «leer después» (todos sabemos cómo acaba eso). Kotlin desde cero: tutorial para principiantes - 1Abrir un IDE y construir algo que funcione de verdad.

Para eso es exactamente este tutorial. Vamos a ir de cero a una primera mini-aplicación funcional. Si conoces Java, irá aún más rápido — voy a ir trazando paralelos todo el tiempo.

Ejecutar Kotlin: dos opciones, una lleva cero minutos

Directamente en el navegador. Entra en play.kotlinlang.org y empieza a escribir. Nada que instalar, nada que configurar, sin perderte en Stack Overflow preguntando por qué no funciona el setup. Perfecto para un primer contacto.

En IntelliJ IDEA. El soporte de Kotlin ya viene integrado. Creas un nuevo proyecto Kotlin — y listo. Sin plugins, sin ceremonias.

Tu primer programa:

fun main() {
    println("Hello, Kotlin!")
}

En Java, lo mismo se ve así:

public class Main {
    public static void main(String[] args) {
        System.out.println("Hello, Kotlin!");
    }
}

Los dos hacen exactamente lo mismo. Kotlin simplemente no tiene public static void ni la clase envolvente — porque... ¿para qué las necesitábamos? Buena pregunta. Java nunca la respondió del todo.

val y var: dos palabras para recordar

En Kotlin, las variables se declaran con val o var.

val name = "Carlos"  // Inmutable. Como final en Java.
var age = 25         // Mutable.

age = 26             // OK.
name = "Ana"         // Error de compilación.

Regla sencilla: usa val en todos los sitios donde el valor no necesita cambiar. var solo cuando realmente necesitas mutación. Es como hacerte una promesa por adelantado: «Esto no lo voy a cambiar.» Y Kotlin se asegura de que la cumplas.

No hace falta especificar el tipo — Kotlin lo infiere:

val score = 100      // Kotlin entiende: esto es un Int
val name = "Carlos"  // Esto es un String

O de forma explícita, si lo prefieres:

val score: Int = 100
val name: String = "Carlos"

Null-safety: cómo Kotlin mata el NullPointerException

Tony Hoare inventó null en 1965 y luego lo llamó su «error del billón de dólares». Suena a autocrítica — pero en realidad se queda corto. El NullPointerException le ha costado a la industria mucho más que eso.

En Java, cualquier variable puede resultar ser null. El compilador no dice nada. Te enteras en runtime. En producción. A veces durante una demo en vivo con un cliente — sí, eso también pasa.

Kotlin resuelve esto a nivel del sistema de tipos:

var name: String = "Carlos"
name = null  // Error de compilación. Inmediatamente. Antes de ejecutar nada.

Si una variable puede ser null, hay que indicarlo explícitamente con ?:

var name: String? = "Carlos"
name = null  // Acordamos que es nullable — ahora sí está bien.

¿Y qué hacemos cuando necesitamos llamar a un método en algo que podría ser null?

println(name?.length)        // Si es null — devuelve null. No peta.
println(name?.length ?: 0)   // Si es null — devuelve 0. Operador Elvis.

Se llama operador Elvis — porque ?: recuerda al peinado de Elvis Presley. No es una broma. Es la explicación oficial en la documentación.

Funciones

En Java:

public static int add(int a, int b) {
    return a + b;
}

En Kotlin:

fun add(a: Int, b: Int): Int {
    return a + b
}

O aún más corto — para funciones de una sola expresión:

fun add(a: Int, b: Int) = a + b

Kotlin soporta valores de parámetros por defecto. En Java tendrías que escribir varios métodos sobrecargados. En Kotlin — solo uno:

fun greet(name: String, greeting: String = "Hola") {
    println("$greeting, $name!")
}

greet("Carlos")          // Hola, Carlos!
greet("Ana", "Ey")       // Ey, Ana!

Templates de string — $name directamente dentro del string. Kotlin sustituye el valor de la variable en su sitio. Sin +, sin concatenaciones — como debería funcionar un lenguaje sensato.

Data class: adiós al boilerplate

Tarea clásica: necesitas un modelo — un usuario con nombre, edad y email.

En Java eso significa un constructor, tres getters, tres setters, equals(), hashCode(), toString(). O Lombok. O resignarse y escribirlo todo a mano como si fuera 2005.

En Kotlin:

data class User(val name: String, val age: Int, val email: String)

Una línea. El compilador genera todo lo demás, incluido copy():

val carlos = User("Carlos", 30, "carlos@example.com")
val olderCarlos = carlos.copy(age = 31)

println(carlos)
// User(name=Carlos, age=30, email=carlos@example.com)

Fíjate en copy(). Creas un objeto nuevo cambiando solo los campos que necesitas. En Java normalmente escribirías un Builder para esto. O simplemente sufrirías.

Extension functions: añadir métodos a clases ajenas

Imagina que quieres añadir un método a la clase String. En Java — una clase utilitaria con métodos estáticos y luego en todas partes escribes StringUtils.doSomething(myString). Funciona, pero huele a parche.

En Kotlin — extension functions:

fun String.isValidEmail(): Boolean {
    return this.contains("@") && this.contains(".")
}

println("user@example.com".isValidEmail())  // true
println("esto-no-es-email".isValidEmail())  // false

Se lee como un método nativo de la clase. Sin herencia, sin wrappers. Simplemente añadiste un método — y ya lo usas.

Juntando todo: un validador de usuarios

Tomemos todo lo que hemos visto y escribamos algo real.

data class User(val name: String, val email: String?)

fun String.isValidEmail() = contains("@") && contains(".")

fun validateUser(user: User): String {
    val emailStatus = user.email?.let {
        if (it.isValidEmail()) "válido" else "inválido"
    } ?: "no proporcionado"

    return "${user.name}: email $emailStatus"
}

fun main() {
    val users = listOf(
        User("Carlos", "carlos@example.com"),
        User("Ana", "ana-no-email"),
        User("Miguel", null)
    )
    users.forEach { println(validateUser(it)) }
}

Salida:

Carlos: email válido
Ana: email inválido
Miguel: email no proporcionado

Fíjate: null está gestionado sin un solo if. Los operadores ?. y ?: lo hacen de forma elegante. Miguel no proporcionó email — y no hay ningún NPE. La vida es bella.

Esto fue solo el principio

Aquí hemos cubierto los fundamentos: variables, null-safety, funciones, data classes, extension functions.

A partir de aquí se pone más interesante. Corrutinas — código asíncrono sin dolor. Sealed classes — una alternativa potente al switch que no te dejará olvidarte de gestionar ningún caso. Operaciones funcionales sobre colecciones — map, filter, reduce en una línea.

Si quieres aprender Kotlin de forma sistemática y no en trozos dispersos por pestañas del navegador — prueba nuestro curso. 62 niveles, más de 1.100 ejercicios con verificación automática, 3 proyectos para tu portfolio. El primer nivel es gratuito.

codegym.cc/es/courses/kotlin

Sigue leyendo