La versión 1.0 de Kotlin está di­s­po­ni­ble desde 2016, pero ya goza de gran prestigio como al­te­r­na­ti­va a Java. El lenguaje de pro­gra­ma­ción basado en objetos de JetBrains, empresa checa de de­sa­rro­llo de software, convence por su agilidad y por la escasez de errores de tiempo de ejecución, en concreto los temidos Nu­ll­Poi­n­te­rE­x­ce­p­tio­ns. El nuevo lenguaje de pro­gra­ma­ción Kotlin es muy popular para de­sa­rro­llar apli­ca­cio­nes Android, pero también como base de apli­ca­cio­nes Ja­va­S­cri­pt.

Kotlin tiene estrechos vínculos con Java: si bien ambos lenguajes de pro­gra­ma­ción no son co­m­pa­ti­bles entre ellos, Kotlin se puede tra­n­s­fo­r­mar en Bytecode que puede leer cualquier máquina virtual Java (JVM).

Manual de Kotlin – con ejemplos

Para iniciarte en Kotlin, puedes descargar el co­m­pi­la­dor de la página web oficial. Lo más fácil es utilizar un entorno de de­sa­rro­llo integrado (IDE): IntelliJ IDEA (también de JetBrains), Eclipse (con su plugin co­rre­s­po­n­die­n­te), NetBeans y Android Studio, por ejemplo, son co­m­pa­ti­bles 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 tra­ba­ja­n­do. 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 asi­g­na­ción de nombres de los paquetes siga la es­tru­c­tu­ra de di­re­c­to­rios donde estos se en­cue­n­tren. No obstante, se re­co­mie­n­da proceder de este modo para no perder la visión general.

Nota

Kotlin carga los paquetes más im­po­r­ta­n­tes au­to­má­ti­ca­me­n­te en cada proyecto.

A di­fe­re­n­cia de Java, Kotlin también puede importar funciones se­le­c­cio­na­das de otros paquetes. Para este fin se facilita la ruta correcta:

import foo.bar.myFunction

A co­n­ti­nua­ción, se puede utilizar la función con toda no­r­ma­li­dad.

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 in­tro­du­cen con val; las otras variables, cuyo valor puede alterarse más adelante, se in­tro­du­cen con var.

val name = "Clara Oswald"
var age = 22

Al contrario del nombre, que es inal­te­ra­ble, 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 ca­ra­c­te­ri­zar co­n­cre­ta­me­n­te estos Basic Types.

Tipos básicos (Basic Types)

Kotlin trabaja con distintos tipos de variables y clases. Cada tipo co­n­s­ti­tu­ye un objeto, y en este punto Kotlin se di­fe­re­n­cia un poco de Java. Mientras que, con el lenguaje de pro­gra­ma­ción más antiguo, los tipos pri­mi­ti­vos primero deben em­pa­que­tar­se 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 in­tro­du­cir números sin una marca de­te­r­mi­na­da: el co­m­pi­la­dor comprende que se trata de valores numéricos. Las comas se re­pre­se­n­tan por puntos. Si lo deseas, también puedes utilizar números he­xa­de­ci­ma­les. Para una mejor le­gi­bi­li­dad, los se­pa­ra­do­res de miles se señalizan mediante guiones bajos. Kotlin di­fe­re­n­cia 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 es­pe­ci­fi­car 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. Mo­di­fi­cá­n­do­lo, 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 ca­ra­c­te­res. En Kotlin se ide­n­ti­fi­can de­li­mi­ta­n­do el texto con comillas dobles. Si deseas in­tro­du­cir 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 pro­gra­ma­ción, en Kotlin se permite el uso de escape cha­ra­c­te­rs: con una barra invertida se marca un carácter que no pertenece pro­pia­me­n­te al string, sino que debe tratarse como carácter de control; y viceversa, mediante la barra invertida también pueden in­tro­du­ci­r­se ca­ra­c­te­res en el string que en Kotlin en realidad tienen otro si­g­ni­fi­ca­do. Estos son los escape cha­ra­c­te­rs 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 in­tro­du­cir el comodín en strings, que puede haberse es­ta­ble­ci­do pre­via­me­n­te como variable. Co­n­si­guie­n­te­me­n­te, en la salida, el comodín se sustituye por un valor real.

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

Ca­ra­c­te­res (Cha­ra­c­te­rs)

Para ca­ra­c­te­res in­di­vi­dua­les, 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 Co­n­s­tru­c­tor 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 co­n­s­tru­c­tor genera una matriz de seis cifras que empieza en cero: 0, 1, 2, 3, 4, 5.

Nota

Más adelante en este texto figura in­fo­r­ma­ción adicional sobre co­n­s­tru­c­to­res 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 nu­me­ra­ción empieza en 0.

Ope­ra­do­res

Como en otros lenguajes de pro­gra­ma­ción, Kotlin también trabaja con varios ope­ra­do­res que pueden in­co­r­po­rar­se a tu código fuente. Entre ellos figuran los ope­ra­do­res ma­te­má­ti­cos (+, -, *, /, %), los ope­ra­do­res re­la­cio­na­les (<, >, <=, >=, ==, !=) y los ope­ra­do­res lógicos (&&, ||, !). Es­tre­cha­me­n­te vi­n­cu­la­das con los ope­ra­do­res están las llamadas keywords (palabras clave), conceptos que en Kotlin tienen un si­g­ni­fi­ca­do es­ta­ble­ci­do y no se pueden in­te­r­pre­tar de ninguna otra forma.

Consejo

Una lista completa de todos los ope­ra­do­res y keywords se encuentra en la do­cu­me­n­ta­ció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 co­n­se­cu­ti­vos 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 es­pe­cí­fi­cos 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 en­co­n­tra­rás más in­fo­r­ma­ción sobre funciones, bucles y búsquedas.

Funciones (Functions)

Las funciones en Kotlin se crean siempre con el comando fun. A co­n­ti­nua­ción, se define el nombre de la función, los ar­gu­me­n­tos que contiene y fi­na­l­me­n­te, 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 pa­rá­me­tros 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. Fi­na­l­me­n­te, en la función main accedemos a la función definida an­te­rio­r­me­n­te, le asignamos valores concretos y mediante println (print line) la consola muestra el resultado. Kotlin procesa el contenido de main() au­to­má­ti­ca­me­n­te. Esta función co­n­s­ti­tu­ye el punto de entrada en los programas de Kotlin.

Hecho

Aparte de funciones, Kotlin no acepta comandos. En este lenguaje, solo se permiten de­cla­ra­cio­nes.

En Kotlin, las funciones que solo abarcan una línea de código se pueden re­pre­se­n­tar de forma si­m­pli­fi­ca­da. En lugar de abrir una llave, escribir una nueva línea in­te­r­ca­la­da 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 pa­rá­me­tros co­rre­s­po­n­die­n­tes 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 di­re­c­ta­me­n­te en otras funciones o variables. Se llaman sin utilizar la keyword fun. Las lambdas en principio se pueden in­tro­du­cir como variables del tipo val y también se crean así.

fun main() {
	val myMessage = { println("¡Hola mundo!") }
	myMessage()
}

Las ex­pre­sio­nes lambda en Kotlin siempre deben in­tro­du­ci­r­se entre llaves. Las lambdas también pueden procesar ar­gu­me­n­tos de funciones. Se re­pre­se­n­tan mediante una flecha que separa los pa­rá­me­tros 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, si­m­ple­me­n­te se introduce la palabra clave class. A co­n­ti­nua­ción, se puede entrar la in­fo­r­ma­ció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 co­n­s­tru­c­tor es una función necesaria para la creación de objetos. El lenguaje di­fe­re­n­cia entre co­n­s­tru­c­to­res primarios y se­cu­n­da­rios: los primarios consisten en una forma abreviada de notación, mientras que, en los se­cu­n­da­rios, la notación se asemeja a la de otros lenguajes orie­n­ta­dos a objetos, entre ellos Java. El ejemplo anterior pertenece a la segunda variante.

También existe la po­si­bi­li­dad de pre­s­ci­n­dir del co­n­s­tru­c­tor se­cu­n­da­rio y usar el co­n­s­tru­c­tor primario en su lugar. Este se escribe di­re­c­ta­me­n­te en el en­ca­be­za­do de la clase y así al mismo tiempo también se in­tro­du­cen los pa­rá­me­tros de la clase. De este modo se reducen si­g­ni­fi­ca­ti­va­me­n­te el número de líneas de código.

class Tardis constructor(var year: Int, var place: String)

Si no se desea es­pe­ci­fi­car el estatus de la vi­si­bi­li­dad 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 in­tro­du­cir esta clase en tu código fuente adicional e in­co­r­po­rar­le los valores concretos.

val tardis1 = Tardis(2133, "Dunlop Station")
val tardis2 = Tardis(1885, "Northhampton")

Como es habitual en la mayoría de los lenguajes orie­n­ta­dos a objetos, se puede acceder di­re­c­ta­me­n­te a las pro­pie­da­des o métodos de un objeto in­tro­du­cie­n­do 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 pe­cu­lia­ri­dad 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 su­fi­cie­n­te para este propósito.

data class User (var username: String, var name: String, var age: Int)

Esta clase se puede aplicar di­re­c­ta­me­n­te.

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 in­s­ta­n­cias que solo se pueden definir una vez (singleton). No­r­ma­l­me­n­te 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 equi­va­le­n­tes en otros lenguajes de pro­gra­ma­ción. Un bucle while se mantendrá en fu­n­cio­na­mie­n­to hasta que una condición de­te­r­mi­na­da 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 di­fe­re­n­cia radica en que el contenido del bucle se va a ejecutar como mínimo una vez, ya que la ve­ri­fi­ca­ción no se realiza hasta el final.

fun main() {
    var n = 1
    do {
        n++
    }	
    while (n < 1)
    println(n)
}

El bucle for sigue en fu­n­cio­na­mie­n­to mientras una condición se mantenga verdadera.

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

Co­n­di­cio­nes (Co­n­di­tio­ns)

Kotlin dispone de tres po­si­bi­li­da­des distintas para realizar enu­n­cia­dos o ra­mi­fi­ca­cio­nes co­n­di­cio­na­les: 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 in­tro­du­ces una acción que debe rea­li­zar­se 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 pe­cu­lia­ri­dad de Kotlin: de­pe­n­die­n­do de ci­r­cu­n­s­ta­n­cias diversas, se realizan di­fe­re­n­tes acciones. De esta forma, la expresión when tiene un efecto parecido a switch en otros lenguajes de pro­gra­ma­ció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 di­re­c­ta­me­n­te a when, evitando así nombrarlo cada vez en el cuerpo. Además, una sola condición puede des­en­ca­de­nar varias acciones. Esto se hace creando un nuevo cuerpo con llaves. Para excluir casos ine­s­pe­ra­dos, 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")    
    }
}

Nu­lla­bi­li­ty

Un notable factor de des­en­ca­n­to en la pro­gra­ma­ción con Java es el error Nu­ll­Poi­n­te­rE­x­ce­p­tion. Aparece cuando se hace re­fe­re­n­cia a un objeto cuyo valor es null. Kotlin elude este problema im­pi­die­n­do que las variables adopten el valor null. Si se da el caso, ya en la co­m­pi­la­ción aparece la nota: “Null can not be a value of a non-null type String“ –u otra ad­ve­r­te­n­cia análoga.

Ahora bien, en ocasiones se desea in­tro­du­cir el valor null in­te­n­cio­na­da­me­n­te. Para estos casos, Kotlin dispone del safe call operator: ?.

fun main() {
	var password: String? = null
	print(password)
}

De este modo, se autoriza a Kotlin ex­plí­ci­ta­me­n­te a aceptar null. A co­n­ti­nua­ción, el programa va a emitir null. Pero si quieres llamar a una cierta propiedad de la variable, tendrás que in­tro­du­cir 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 in­tro­du­cir un valor al­te­r­na­ti­vo. Para ello, se utiliza el llamado Operador Elvis: ?: (se llama así porque los ca­ra­c­te­res 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.

Ir al menú principal