Kotlin: tutorial sobre el nuevo lenguaje de programación

La versión 1.0 de Kotlin está disponible desde 2016, pero ya goza de gran prestigio como alternativa a Java. El lenguaje de programación basado en objetos de JetBrains, empresa checa de desarrollo de software, convence por su agilidad y por la escasez de errores de tiempo de ejecución, en concreto los temidos NullPointerExceptions. El nuevo lenguaje de programación Kotlin es muy popular para desarrollar aplicaciones Android, pero también como base de aplicaciones JavaScript.
Kotlin tiene estrechos vínculos con Java: si bien ambos lenguajes de programación no son compatibles entre ellos, Kotlin se puede transformar en Bytecode que puede leer cualquier máquina virtual Java (JVM).

Manual de Kotlin – con ejemplos

Para iniciarte en Kotlin, puedes descargar el compilador de la página web oficial. Lo más fácil es utilizar un entorno de desarrollo integrado (IDE): IntelliJ IDEA (también de JetBrains), Eclipse (con su plugin correspondiente), NetBeans y Android Studio, por ejemplo, son compatibles con Kotlin.
Consejo
Los creadores de Kotlin facilitan un entorno de pruebas online, donde puedes probar todos los ejemplos.

Paquetes (Packages)

Al iniciar un proyecto, has de importar los paquetes que vas a necesitar y definir el paquete en el que estás trabajando. Los paquetes contienen clases y funciones.

package test.bar
import foo.bar
import footoo.bar as footoo
Para evitar problemas con nombres repetidos, puedes cambiar los nombres de paquetes en Kotlin con as. No es necesario que la asignación de nombres de los paquetes siga la estructura de directorios donde estos se encuentren. No obstante, se recomienda proceder de este modo para no perder la visión general.
Nota
Kotlin carga los paquetes más importantes automáticamente en cada proyecto.
A diferencia de Java, Kotlin también puede importar funciones seleccionadas de otros paquetes. Para este fin se facilita la ruta correcta:

import foo.bar.myFunction
A continuación, se puede utilizar la función con toda normalidad.
Consejo
Las líneas de código no deben cerrarse en Kotlin con una marca, por ejemplo, un punto y coma.

Variables

Kotlin distingue dos tipos distintos de variables: las fijas, que solo se pueden leer, se introducen con val; las otras variables, cuyo valor puede alterarse más adelante, se introducen con var.

val name = "Clara Oswald"
var age = 22
Al contrario del nombre, que es inalterable, la edad se puede ajustar, por ejemplo, en una función.
Nota
En este ejemplo, Kotlin ha fijado el tipo del valor de las variables. También es posible caracterizar concretamente estos Basic Types.

Tipos básicos (Basic Types)

Kotlin trabaja con distintos tipos de variables y clases. Cada tipo constituye un objeto, y en este punto Kotlin se diferencia un poco de Java. Mientras que, con el lenguaje de programación más antiguo, los tipos primitivos primero deben empaquetarse en un wrapper para que se comporten como objetos, esto no es necesario en Kotlin. Aquí todos los tipos ya son realmente objetos.

Números (Numbers)

En Kotlin se pueden introducir números sin una marca determinada: el compilador comprende que se trata de valores numéricos. Las comas se representan por puntos. Si lo deseas, también puedes utilizar números hexadecimales. Para una mejor legibilidad, los separadores de miles se señalizan mediante guiones bajos. Kotlin diferencia entre distintos tipos de números que pueden asumir diversos valores máximos:
  • Long: 64 Bit
  • Int: 32 Bit
  • Short: 16 Bit
  • Byte: 8 Bit
  • Double: 64 Bit
  • Float: 32 Bit
Los números de coma flotante de Double y Float se comportan de forma distinta a los números de coma fija en cálculos complejos. Como los tipos, puedes especificar los números en tu código.

val myNumber: Long = 40
Es posible convertir un número de un tipo a otro distinto.

val myInt = 600
val myLong= myInt.toLong()
La orden toLong convierte el valor entero en un valor Long. Modificándolo, el comando también funciona para otros tipos de números.

String

Un string consiste en palabras o frases enteras, es decir, una secuencia de caracteres. En Kotlin se identifican delimitando el texto con comillas dobles. Si deseas introducir varias líneas de texto, hacen falta tres comillas dobles al principio y al final de la secuencia (Raw String).

val myString = "Esto es un string de una sola línea."
val myLongString = """Esto es un string,
de más de una línea."""
Como en muchos otros lenguajes de programación, en Kotlin se permite el uso de escape characters: con una barra invertida se marca un carácter que no pertenece propiamente al string, sino que debe tratarse como carácter de control; y viceversa, mediante la barra invertida también pueden introducirse caracteres en el string que en Kotlin en realidad tienen otro significado. Estos son los escape characters posibles:
  • \t: tabulador
  • \b: retroceso
  • \n: nueva línea
  • \r: salto de línea
  • \': comilla simple
  • \": comilla doble
  • \\: barra invertida
  • \$: símbolo del dólar
El símbolo del dólar sirve para introducir el comodín en strings, que puede haberse establecido previamente como variable. Consiguientemente, en la salida, el comodín se sustituye por un valor real.

val author = "Sandra"
val myString = "El autor de este texto es $author"

Caracteres (Characters)

Para caracteres individuales, Kotlin, además de strings, también dispone del tipo especial de datos character. En lugar de comillas dobles, se utilizan comillas simples.

var model = 'A'

Booleano

El tipo básico booleano expresa un valor que puede ser o bien verdadero (true) o falso (false).

Matrices de datos

En Kotlin, un array es una matriz de datos. Una matriz de datos se construye con arrayOf() o Array(). Es fácil crear la primera función:

val myArray1 = arrayOf(0, 1, 2, 3, 4, 5)
Con este código se genera una matriz de datos con las cifras del 1 al 5. Estas matrices también pueden albergar otros tipos, como strings y booleanos –incluso mezclados. Si se desea limitar la matriz a un solo tipo, se indica en la función.

val myArray2 = arrayOf<int>(10, 20, 30)</int>
val myArray3 = booleanArrayOf(true, true, false)
El Kotlin Constructor Array() es más complejo: aquí también hay que facilitar la longitud y una función lambda.

val myArray4 = Array(6, { i -> i })
El constructor genera una matriz de seis cifras que empieza en cero: 0, 1, 2, 3, 4, 5.
Nota
Más adelante en este texto figura información adicional sobre constructores y lambdas.
Cada entrada en una matriz está indexada y se puede acceder a la misma a través de este índice. Para ello se utilizan corchetes, donde se introduce la cifra que la entrada ocupa en el listado.

fun main() {
	val myArray5 = arrayOf("Jan", "Maria", "Samuel")
	println(myArray5[2])
}
Nota
En este caso, la función va a dar como resultado "Samuel", ya que la numeración empieza en 0.

Operadores

Como en otros lenguajes de programación, Kotlin también trabaja con varios operadores que pueden incorporarse a tu código fuente. Entre ellos figuran los operadores matemáticos (+, -, *, /, %), los operadores relacionales (<, >, <=, >=, ==, !=) y los operadores lógicos (&&, ||, !). Estrechamente vinculadas con los operadores están las llamadas keywords (palabras clave), conceptos que en Kotlin tienen un significado establecido y no se pueden interpretar de ninguna otra forma.
Consejo
Una lista completa de todos los operadores y keywords se encuentra en la documentación oficial de Kotlin.

Rangos

En Kotlin, un rango define a un tipo que va de un punto concreto a otro. Para crear un rango, se utiliza el operador .. o las funciones rangeTo() o bien downTo(). La variante con dos puntos consecutivos puede elevar a potencias. En cambio, con las funciones se define en una dirección.

val range1 = 1..5
val range2 = 1.rangeTo(5)
val range3 = 5.downTo(1)
Con estas versiones sencillas se crea un rango con pasos de uno en uno. Para modificar el tamaño de los pasos, se añade la función step.

val range4 = 0..10 step(2)
Para dirigirse a datos específicos del rango, se usa el operador in. De este modo se puede crear, por ejemplo, una búsqueda o un bucle. Para comprobar si un valor forma parte del rango se aplica el operador !in.

val range5 = 0..10
fun main() {
	for (n in range5) {
		println(n)
	}
	if (7 in range5) {
		println("yes")
	}
	if (12 !in range5) {
		println("no")
	}
}
Nota
Más adelante encontrarás más información sobre funciones, bucles y búsquedas.

Funciones (Functions)

Las funciones en Kotlin se crean siempre con el comando fun. A continuación, se define el nombre de la función, los argumentos que contiene y finalmente, lo que hace.

fun div(a: Int, b: Int): Int {
	return a/b
}
fun main() {
	println(div(100, 2))
}
Primero definimos la función div (división) con dos parámetros enteros a y b. La función nos dará como resultado la división de a por b, también en la forma de una variable entera. Finalmente, en la función main accedemos a la función definida anteriormente, le asignamos valores concretos y mediante println (print line) la consola muestra el resultado. Kotlin procesa el contenido de main() automáticamente. Esta función constituye el punto de entrada en los programas de Kotlin.
Hecho
Aparte de funciones, Kotlin no acepta comandos. En este lenguaje, solo se permiten declaraciones.
En Kotlin, las funciones que solo abarcan una línea de código se pueden representar de forma simplificada. En lugar de abrir una llave, escribir una nueva línea intercalada y cerrar de nuevo la llave, se emplea un signo igual. Además, así no hace falta la orden return.

fun div(a: Int, b: Int): Int = a/b
fun main() = println(div(100, 2))
Para evitar errores si falta un parámetro, al definir la función se pueden dar valores estándar. Si, al activar la función, se dejan los parámetros correspondientes en blanco, se utilizan los valores por defecto.

fun div(a: Int = 10, b: Int = 5): Int = a/b
fun main() = println(div())

Lambdas

Una función lambda (o función anónima) es una función que no pertenece ni a una clase ni a un objeto. Las lambdas se colocan directamente en otras funciones o variables. Se llaman sin utilizar la keyword fun. Las lambdas en principio se pueden introducir como variables del tipo val y también se crean así.

fun main() {
	val myMessage = { println("¡Hola mundo!") }
	myMessage()
}
Las expresiones lambda en Kotlin siempre deben introducirse entre llaves. Las lambdas también pueden procesar argumentos de funciones. Se representan mediante una flecha que separa los parámetros del núcleo de la expresión.

fun main() {
    val div = {a: Int, b: Int -> a/b}
    println(div(6,2))
}

Clases (Classes)

Al igual que en Java, las clases en Kotlin son matrices de datos y funciones. Para definir una clase, simplemente se introduce la palabra clave class. A continuación, se puede entrar la información de la nueva clase.

class Tardis {
	var year: Int
	var place: String
	constructor(year: Int, place: String) {
		this.year = year
		this.place = place
	}
}
En Kotlin, el constructor es una función necesaria para la creación de objetos. El lenguaje diferencia entre constructores primarios y secundarios: los primarios consisten en una forma abreviada de notación, mientras que, en los secundarios, la notación se asemeja a la de otros lenguajes orientados a objetos, entre ellos Java. El ejemplo anterior pertenece a la segunda variante.
También existe la posibilidad de prescindir del constructor secundario y usar el constructor primario en su lugar. Este se escribe directamente en el encabezado de la clase y así al mismo tiempo también se introducen los parámetros de la clase. De este modo se reducen significativamente el número de líneas de código.

class Tardis constructor(var year: Int, var place: String)
Si no se desea especificar el estatus de la visibilidad de la clase (public, private, protected), se puede evitar dar nombre a la keyword.

class Tardis (var year: Int, var place: String)
Los tres ejemplos de código producen el mismo resultado.
Ya puedes introducir esta clase en tu código fuente adicional e incorporarle los valores concretos.

val tardis1 = Tardis(2133, "Dunlop Station")
val tardis2 = Tardis(1885, "Northhampton")
Como es habitual en la mayoría de los lenguajes orientados a objetos, se puede acceder directamente a las propiedades o métodos de un objeto introduciendo un punto y el nombre de la propiedad o el método detrás del objeto.

class Tardis (var year: Int, var place: String)
val tardis1 = Tardis(2133, "Dunlop Station")
val tardis2 = Tardis(1885, "Northhampton")
fun main() {
    println(tardis1.year)
}
Una peculiaridad de Kotlin es la data class. Este tipo de clases está diseñado para almacenar solo datos. En principio, una línea de código es suficiente para este propósito.

data class User (var username: String, var name: String, var age: Int)
Esta clase se puede aplicar directamente.

data class User (var username: String, var name: String, var age: Int)
fun main() {
    val user1 = User ("River Song", "Melody Pond", 200)
    println("Username: " + user1.username)
    println("Name: " + user1.name)
    println("Age: " + user1.age)
}

Objetos (Objects)

Los objetos en Kotlin son instancias que solo se pueden definir una vez (singleton). Normalmente contienen variables y funciones. Un objeto se crea, de forma parecida a una clase, en principio solo con una línea de código. No obstante, en ese momento el objeto está vacío. El contenido se inserta en su cuerpo.

object myObject {
	fun sum(a: Int, b: Int): Int {
		return a+b
	}
}

Bucles (Loops)

Kotlin dispone de tres tipos distintos de bucles: while, do..while e if. Se comportan como sus equivalentes en otros lenguajes de programación. Un bucle while se mantendrá en funcionamiento hasta que una condición determinada se cumpla.

fun main() {
    var n = 1
    while (n <= 10) {
        println(n++)
    }
}
El bucle do..while se comporta de manera similar a la variante con while. La diferencia radica en que el contenido del bucle se va a ejecutar como mínimo una vez, ya que la verificación no se realiza hasta el final.

fun main() {
    var n = 1
    do {
        n++
    }	
    while (n < 1)
    println(n)
}
El bucle for sigue en funcionamiento mientras una condición se mantenga verdadera.

val myRange = 0..10
fun main() {
	for (n in myRange) {
		print("$n ")
	}
}

Condiciones (Conditions)

Kotlin dispone de tres posibilidades distintas para realizar enunciados o ramificaciones condicionales: if, if..else y when. Con el enunciado if, el ordenador ejecuta una tarea si la condición se cumple.

val whoCompanion = arrayOf("Bill Potts", "Clara Oswald", "Amy Pond", "Martha Jones", "Donna Noble", "Rose Tyler")
fun main() {
    if ("Rose Tyler" in whoCompanion) {
        print("yes")
    }
}
Con else introduces una acción que debe realizarse si la condición no se cumple.

val whoCompanions9 = arrayOf("Rose Tyler")
val whoCompanions10 = arrayOf("Martha Jones", "Donna Noble", "Rose Tyler")
val whoCompanions11 = arrayOf("Clara Oswald", "Amy Pond")
val whoCompanions12 = arrayOf("Bill Potts", "Clara Oswald")
fun main() {
    var whoCompanion = "Clara Oswald"
    if (whoCompanion in whoCompanions9) {
        print("yes")
    }
    else {
        print("no")
    }
}
El término when es a fin de cuentas una peculiaridad de Kotlin: dependiendo de circunstancias diversas, se realizan diferentes acciones. De esta forma, la expresión when tiene un efecto parecido a switch en otros lenguajes de programación, aunque funciona con más precisión.
En el cuerpo de when se colocan los distintos casos a verificar, que siempre están en relación con una variable definida.

var age = 17
fun main() {
    when {
     	age > 18 -> println("¡Eres demasiado mayor!")
     	age == 18 -> println("¡Ya eres mayor!")
     	age == 17 -> println("¡Bienvenido / a!")
     	age <= 16 -> println("¡Eres demasiado joven!")
    }
}
Sin embargo, el argumento también se puede pasar directamente a when, evitando así nombrarlo cada vez en el cuerpo. Además, una sola condición puede desencadenar varias acciones. Esto se hace creando un nuevo cuerpo con llaves. Para excluir casos inesperados, utiliza else.

fun multi(a: Int, b: Int, c: Int): Int {
    return a*b*c
}
fun main() {
    val d = "yes"
    when (d) {
        "no" -> println("No hay cálculo")
        "yes" -> {
            println("Empieza el cálculo") 
            println(multi(5, 2, 100))
            println("Cálculo finalizado")
        }
    else -> println("Entrada errónea")    
    }
}

Nullability

Un notable factor de desencanto en la programación con Java es el error NullPointerException. Aparece cuando se hace referencia a un objeto cuyo valor es null. Kotlin elude este problema impidiendo que las variables adopten el valor null. Si se da el caso, ya en la compilación aparece la nota: “Null can not be a value of a non-null type String“ –u otra advertencia análoga.
Ahora bien, en ocasiones se desea introducir el valor null intencionadamente. Para estos casos, Kotlin dispone del safe call operator: ?.

fun main() {
	var password: String? = null
	print(password)
}
De este modo, se autoriza a Kotlin explícitamente a aceptar null. A continuación, el programa va a emitir null. Pero si quieres llamar a una cierta propiedad de la variable, tendrás que introducir de nuevo un safe call operator.

fun main() {
	var password: String? = null
	print(password?.length)
}
Este código también va a dar como resultado null, pero ningún error, y el programa se ejecuta. Resulta más elegante introducir un valor alternativo. Para ello, se utiliza el llamado Operador Elvis: ?: (se llama así porque los caracteres pueden leerse como un smiley con tupé).

fun main() {
    val firstName = null
    val lastName = "Pond"
	val name: String = firstName?: "Falta el nombre de pila" + " " + ¿primer apellido?: "Falta el primer apellido"
	print(name)
}
En este ejemplo se muestran notas si una variable asume el valor null.
¿Le ha resultado útil este artículo?
Page top