Programación · Lenguajes de programación
Tutorial de Kotlin para principiantes
Respuesta corta
Kotlin es un lenguaje moderno de tipado estático creado por JetBrains que se ejecuta en la JVM y es el lenguaje preferido para el desarrollo en Android. Es conciso, totalmente interoperable con Java y su sistema de tipos evita en tiempo de compilación la mayoría de los errores de puntero nulo. Esta página cubre la instalación, las variables, la null safety, el control de flujo, las funciones, las colecciones, las clases, las corrutinas y el manejo de errores con ejemplos ejecutables.
Qué es Kotlin y para qué se usa
Kotlin es un lenguaje de propósito general con tipado estático creado por JetBrains. Se ejecuta en la máquina virtual de Java (JVM) y también puede compilarse a JavaScript, WebAssembly y código máquina nativo. Kotlin es totalmente interoperable con Java: un archivo Kotlin puede llamar a cualquier biblioteca de Java, y el código Java puede llamar a Kotlin.
Desde 2019 Google recomienda Kotlin como lenguaje preferido para las apps de Android, así que la mayor parte del código nuevo de Android está en Kotlin. También es habitual en el servidor con frameworks como Spring Boot y Ktor, y Kotlin Multiplatform permite a los equipos compartir la lógica de negocio entre Android, iOS, escritorio y web.
Instalar Kotlin y ejecutar Hello, world
La forma más sencilla de empezar es IntelliJ IDEA o Android Studio, que ya incluyen Kotlin. Para la línea de comandos, instala el compilador (por ejemplo con SDKMAN! o Homebrew) y un entorno de ejecución de Java (JDK). Un programa Kotlin empieza en una función main de nivel superior; no hace falta ninguna clase.
// hello.kt
fun main() {
println("Hello, world!")
}kotlinc hello.kt -include-runtime -d hello.jar
java -jar hello.jar
# Hello, world!Para proyectos reales, lo normal es usar herramientas de compilación como Gradle (con Kotlin DSL). También puedes probar código sin instalar nada en el Kotlin Playground en línea.
Variables y tipos básicos
Declara una variable de solo lectura con val y una mutable con var. El compilador infiere el tipo, pero puedes escribirlo después de dos puntos. Prefiere val: hace que el código sea más fácil de razonar.
val name = "Ada" // String, cannot be reassigned
var count: Int = 0 // explicit type, can change
count += 1
val pi = 3.14159 // Double
val big = 9_000_000_000L // Long
println("$name has $count item(s), pi is ${"%.2f".format(pi)}")
// Ada has 1 item(s), pi is 3.14| Tipo | Ejemplo | Notas |
|---|---|---|
Int | 42 | entero de 32 bits con signo |
Long | 42L | entero de 64 bits con signo |
Double | 3.14 | punto flotante de 64 bits |
Boolean | true | true o false |
Char | 'k' | un solo carácter |
String | "text" | inmutable; admite plantillas $name |
Kotlin no amplía los números de forma implícita: un Int no se convierte automáticamente en Long. Llama explícitamente a toLong(), toDouble() o toString().
Null safety
La null safety (seguridad frente a nulos) es la característica estrella de Kotlin. Un tipo normal como String nunca puede contener null. Para permitirlo añades ?, como en String?, y el compilador te obliga a tratar el caso nulo antes de usar el valor.
fun greet(name: String?) {
// println(name.length) // compile error: name may be null
println(name?.length) // safe call: prints null if name is null
val len = name?.length ?: 0 // Elvis operator: default value
if (name != null) {
println(name.uppercase()) // smart cast: name is String here
}
println("length = $len")
}
fun main() {
greet("kotlin") // 6, KOTLIN, length = 6
greet(null) // null, length = 0
}Control de flujo: if, when y bucles
En Kotlin if y when son expresiones, por lo que devuelven un valor. when sustituye a la instrucción switch de Java y C y puede comparar valores, rangos y tipos.
val score = 82
val grade = if (score >= 50) "pass" else "fail"
val letter = when (score) {
in 90..100 -> "A"
in 80..89 -> "B"
in 70..79 -> "C"
else -> "F"
}
println("$grade $letter") // pass B
for (i in 1..3) print("$i ") // 1 2 3
println()
for (i in 10 downTo 0 step 5) print("$i ") // 10 5 0
println()
var n = 3
while (n > 0) { print(n); n-- } // 321
println()1..3 es un rango cerrado que incluye ambos extremos; 0 until 3 (o 0..<3) excluye el final, que es lo que normalmente quieres para los índices.
Funciones y lambdas
Las funciones se declaran con fun. Los parámetros pueden tener valores por defecto, quien llama puede usar argumentos con nombre, y una función cuyo cuerpo es una sola expresión puede usar =.
fun area(width: Double, height: Double = 1.0): Double = width * height
fun main() {
println(area(3.0, 2.0)) // 6.0
println(area(width = 4.0)) // 4.0 (default height)
val square = { x: Int -> x * x } // a lambda stored in a variable
println(square(5)) // 25
val nums = listOf(1, 2, 3, 4, 5)
val evensSquared = nums.filter { it % 2 == 0 }.map { it * it }
println(evensSquared) // [4, 16]
}Cuando una lambda tiene un solo parámetro, puedes referirte a él como it. Cuando el último argumento de una función es una lambda, puede ir fuera de los paréntesis, por eso filter { ... } parece un bloque integrado en el lenguaje.
Colecciones y data classes
La biblioteca estándar separa las colecciones de solo lectura de las mutables: listOf, setOf y mapOf crean colecciones de solo lectura, mientras que mutableListOf, mutableSetOf y mutableMapOf se pueden modificar.
val fruits = mutableListOf("apple", "banana")
fruits.add("cherry")
val ages = mapOf("Ann" to 31, "Bo" to 25)
println(fruits.size) // 3
println(ages["Ann"]) // 31
println(ages["Zed"]) // null (map lookups are nullable)
data class User(val name: String, val age: Int)
val u = User("Ann", 31)
val older = u.copy(age = 32)
println(u) // User(name=Ann, age=31)
println(u == User("Ann", 31)) // true (structural equality)
val (n, a) = older // destructuring
println("$n $a") // Ann 32Una data class obtiene automáticamente equals, hashCode, toString, copy y desestructuración. En Kotlin == compara el contenido (llama a equals); === compara referencias.
Clases, funciones de extensión y corrutinas
Las clases son finales por defecto; marca una clase como open para permitir la herencia. Las funciones de extensión añaden métodos a tipos existentes sin modificarlos, y las clases sealed modelan un conjunto cerrado de casos que when puede comprobar de forma exhaustiva.
fun String.shout(): String = uppercase() + "!"
sealed class Shape
data class Circle(val r: Double) : Shape()
data class Rect(val w: Double, val h: Double) : Shape()
fun area(s: Shape): Double = when (s) {
is Circle -> Math.PI * s.r * s.r
is Rect -> s.w * s.h
} // no else needed: the compiler knows all cases
fun main() {
println("hi".shout()) // HI!
println(area(Rect(2.0, 3.0))) // 6.0
}Para el código asíncrono y concurrente, Kotlin usa las corrutinas de la biblioteca kotlinx.coroutines. Una función suspend puede pausarse sin bloquear un hilo, y constructores como launch y async inician corrutinas dentro de un ámbito. Así hacen las apps de Android las llamadas de red fuera del hilo principal.
Manejo de errores
Kotlin tiene try, catch y finally como Java, pero todas las excepciones son no comprobadas: nunca estás obligado a declararlas ni a capturarlas. try también es una expresión. Para los fallos esperados, la biblioteca estándar ofrece runCatching y Result, y funciones como toIntOrNull() devuelven null en lugar de lanzar una excepción.
fun parse(s: String): Int = try {
s.toInt()
} catch (e: NumberFormatException) {
-1
}
fun main() {
println(parse("42")) // 42
println(parse("x")) // -1
println("7".toIntOrNull()) // 7
println("seven".toIntOrNull()) // null
val r = runCatching { "abc".toInt() }
println(r.isFailure) // true
}Un programa completo y pequeño: frecuencia de palabras
Este programa cuenta cuántas veces aparece cada palabra en un texto e imprime las tres primeras. Usa una extensión de String, funciones de colección y una data class.
data class WordCount(val word: String, val count: Int)
fun topWords(text: String, n: Int): List<WordCount> =
text.lowercase()
.split(Regex("[^a-z']+"))
.filter { it.isNotBlank() }
.groupingBy { it }
.eachCount()
.map { (w, c) -> WordCount(w, c) }
.sortedWith(compareByDescending<WordCount> { it.count }.thenBy { it.word })
.take(n)
fun main() {
val text = "The cat and the dog. The dog sat, and the cat ran."
for ((word, count) in topWords(text, 3)) {
println("$word: $count")
}
}
// the: 4
// and: 2
// cat: 2Siguientes pasos
- La documentación oficial de Kotlin en kotlinlang.org, empezando por Basic syntax y el Kotlin tour.
- Kotlin Koans, un conjunto de ejercicios cortos que se ejecutan en el navegador.
- Las guías de Android Developers para Kotlin en Android, o las guías de Ktor y Spring para Kotlin en el servidor.
- La guía de kotlinx.coroutines cuando necesites código asíncrono.
Preguntas frecuentes
¿Es Kotlin mejor que Java?
Kotlin es más conciso y añade null safety, data classes y corrutinas, pero se ejecuta en la misma JVM y usa las mismas bibliotecas. Muchos equipos mezclan ambos lenguajes en un mismo proyecto, así que la elección es cuestión de estilo y productividad, no de capacidad.
¿Necesito aprender Java antes que Kotlin?
No. Puedes aprender Kotlin como tu primer lenguaje para la JVM. Conocer algo de Java ayuda al leer código antiguo de Android o la documentación de bibliotecas de Java.
¿Cuál es la diferencia entre val y var?
val declara una referencia de solo lectura que no se puede reasignar tras la inicialización, y var declara una que sí se puede reasignar. Si un val apunta a una lista mutable, su contenido aún se puede modificar.
¿Se puede usar Kotlin para apps de iOS?
Sí, mediante Kotlin Multiplatform, que compila el código Kotlin compartido a código nativo para iOS. La interfaz de usuario suele escribirse todavía en Swift, aunque Compose Multiplatform también permite compartir código de UI.
¿Qué hace el operador ?: en Kotlin?
Es el operador Elvis: a ?: b devuelve a si no es nulo y, en caso contrario, devuelve b. Se combina con frecuencia con las llamadas seguras, como en name?.length ?: 0.