Kotlin: La Máquina Virtual de Java tiene un nuevo aliado

Kotlin: La Máquina Virtual de Java tiene un nuevo aliado
Sin comentarios Facebook Twitter Flipboard E-mail

Una de las grandes virtudes de la Máquina Virtual de Java (JVM) es que nos da la habilidad de utilizar cualquier lenguaje que sea capaz de compilar bytecode. Existen infinidad de lenguajes que se ejecutan sobre la JVM, entre los que destacan algunos como Scala, Clojure o Groovy.

Pero desde hace unos meses, tenemos un nuevo sheriff en la ciudad.

Kotlin, el lenguaje de programación de Jetbrains

Si te mueves en algunos sectores como el desarrollo de Apps en Android, seguramente últimamente hayas oído hablar de Kotlin.

Kotlin es un lenguaje creado por Jetbrains, la empresa detrás de Intellij, el famoso IDE para Java. Es un lenguaje muy sencillo de aprender y de utilizar. Uno de sus principales objetivos es el de ofrecer una serie de características simples pero con una gran potencialidad.

Es un lenguaje orientado a objetos con algunas características funcionales.

¿Por qué surge Kotlin?

Te estarás preguntando cuál es la necesidad de crear otro lenguaje de programación. La gente de Jetbrains se hizo esa misma pregunta hace unos años, y llegaron a la conclusión de que necesitaban para sus proyectos un lenguaje que fuese mucho más expresivo que el anticuado Java para ser más productivos.

Así que ni cortos ni perezosos, cogieron el famoso libro de Joshua Bloch, Effective Java, y solucionaron con Kotlin la mayor parte de los problemas en Java que su propio diseñador había detectado.

Estas son algunas de sus características:

  • Es ligero: a diferencia de otros lenguajes que se construyen sobre la JVM, el equipo de Kotlin se preocupa de que su librería sea lo más pequeña posible. De esta forma es fácil de usar en entornos como Android, donde el límite de métodos siempre es un dolor de cabeza.

  • Es interoperable: como Jetbrains quería usarlo internamente para todos sus productos, uno de los objetivos principales era que pudieran utilizarlo con su base de código existente. De esta forma, aunque desarrollemos en Kotlin, podemos seguir utilizando todas las librerías existentes en Java. Y podemos, incluso, mezclar los dos lenguajes en un mismo proyecto.

  • Es un lenguaje fuertemente tipado: Aunque rara vez tendrás que indicar mientras escribes cuál es el tipo. El compilador será capaz de inferir los tipos de las variables por ti, obteniendo lo mejor de los dos mundos: un código seguro y conciso. Además, todos los tipos son objetos.

  • Es seguro frente a nulos: uno de los mayores problemas en Java son los valores nulos. No hay forma de especificar cuándo aceptamos que una variable pueda ser _null_, por lo que tendemos a tener cientos de NullPointerException inesperados. Kotlin usa nulidad explícita, lo que nos obligará a comprobar si un valor es nulo antes de usarlo.

La versión 1.0 del lenguaje fue lanzada el 15 de febrero de 2016, tras más de 5 años de desarrollo.

Sintaxis de Kotlin

Aunque aquí podría hablar largo y extenso sobre todas las diferencias que podemos encontrar entre Java y Kotlin, voy a tratar de sintetizar algunas de las más relevantes.

Properties

Al igual que muchos otros lenguajes modernos, Kotlin utiliza properties en lugar de campos para definir los miembros de una clase. Una property es lo que en Java sería un campo + getter + setter. De esta forma en nuestra clase podríamos tener algo así:


var name: String = "MyName"

Esto nos permite asignar y recoger su valor como si fuera un campo público en Java, pero con la posibilidad de sobrescribir el getter o el setter en cualquier momento:


var name: String
    get() = "Name: $field"

Algunas otras cosas interesantes a ver aquí:

  • No es necesario usar punto y coma para finalizar las líneas
  • Podemos añadir código directamente a los Strings añadiendo el símbolo $ delante, de tal forma que nos evitamos cosas como el String.format() de Java.
  • Podemos marcar nuestras properties y variables con var, lo que significa que la variable es mutable (tiene getter y setter) o con val, que hará la variable inmutable (sólo tiene getter).

Constructor

Aunque se nos da la posibilidad de usar varios constructores, la idea en Kotlin es que utilicemos sólo uno. De esta forma, se nos da la opción de definirlo directamente tras el nombre de la clase, de esta forma:


class Contact(name: String, imageUrl: String)

Si los parámetros del constructor van a ser las propiedades de la clase, simplemente tenemos que marcarlos con val o var


class Contact(val name: String, val imageUrl: String)

¿Qué ocurre si necesitamos dos sobrecargas de contacto? Imaginemos que los contactos pueden no tener imagen. Esto se puede hacer fácilmente porque le podemos asignar valores por defecto a los parámetros de una función:


class Contact(val name: String, val imageUrl: String = DEFAULT_URL)

Desde este momento, ya podemos crear contactos de dos formas distintas:


val contact = Contact("Peter")
val contact2 = Contact("John", "http://...")

Un par de cosas a notar aquí:

  • Como ves, no hace falta la palabra new para crear un objeto.
  • No es necesario especificar el tipo de la variable que estamos creando, el compilador es capaz de inferirlo.

Data Class: las clases POJO de una línea

En Java, si queremos crear una clase de almacenamiento de estado, necesitamos definir un constructor, los campos, los getters y los setters, el toString(), el equals() o el hashCode() entre otros. Con Kotlin podemos hacer todo esto en una línea:


data class Contact(val name: String, val imageUrl: String = DEFAULT_URL)

Esta clase implementa todas esas funciones basada en las properties que define, y además añade otras características como un método copy o la posibilidad de descomposición en variables:


val (name, image) = contact

Seguridad frente a nulos

Ya hemos hablado de que Kotlin es segura frente a nulos. ¿Pero cómo funciona? Si queremos que una variable pueda ser nula, tendremos que marcar el tipo con una interrogación. A partir de ese momento, tendremos que comprobar si la variable no es null antes de poder usarla:


val contact: Contact? = null
contact.print()

El código anterior no compilará. Si queremos que funcione, necesitamos comprobar que no es null:


val contact: Contact? = null
if (contact != null) {
    contact.print()
}

Otra propiedad interesante de Kotlin es el auto-casting. Kotlin hace casting automático siempre que es posible. Por ejemplo, dentro del if, el contacto se comporta como si fuera de tipo Contact y no Contact?. Esto es muy útil cuando comprobamos por ejemplo si un objeto es de un tipo. No necesitaremos hacer casting para empezar a usarlo como tal.

Volviendo al tema de la nulidad, tenemos una forma más condensada de hacer lo anterior:


val contact: Contact? = null
contact?.print()

En ese caso, la función print() sólo se ejecutará si el contacto no es nulo.

Finalmente, podemos saltarnos todas esas comprobaciones y decirle al compilador que estamos seguros de que esa variable no es null. No conviene abusar de este operador, ya que volveremos a encontrarnos con los mismos problemas que en Java si lo hacemos:


val contact: Contact? = null
contact!!.print()

Ese código fallará en ejecución, pero compilará sin problemas.

Extension functions

Las funciones de extensión son otra cosa muy curiosa de Kotlin, y que hará que tu código sea mucho más limpio. Vienen a sustituir a las típicas clases de utils con métodos estáticos que nos creamos en Java. En Kotlin se pueden añadir funciones nuevas a clases a las que no tenemos acceso de forma muy sencilla:


fun String.hassel(): String {
    return "${this}hoff"
}

Como ves, tan sólo hay que indicar que la función pertenece a String, y podremos usar this en el cuerpo de la misma. Esta función añadirá "hoff" a cualquier String. Podemos usarla como si fuera una función más de la clase:


val x = "hello".hassel()

Por supuesto, esto no modifica el código real de la clase, añadirá un import a la clase donde se use. También se puede escribir en una sola línea:


fun String.hassel() = "${this}hoff"

Algunas cosas a tener en cuenta:

  • Las funciones se identifican con fun

  • Si usamos la asignación para definir la función, no hace falta especificar el tipo de retorno.

Lambdas

Las lambdas son ya muy conocidas en el mundo de la programación, ya que muchos lenguajes las incluyen. En Java, no ha sido hasta Java 8 que no se han añadido lenguaje, pero en Kotlin nos ofrecen algunas características extra.

Una lambda es básicamente una función anónima en la que definimos únicamente los parámetros de entrada y el de salida de la misma. Por ejemplo, una función que recibe un String y un entero y devuelve un String con ambos concatenados se podría definir así:


val f: (String, Int) -> String = { s, i -> "$s $i" }

Por supuesto, si estamos asignando un valor, podemos evitar especificar el tipo indicando los tipos de las variables:


val f = { s: String, i: Int -> "$s $i" }

Las lambdas se pueden pasar como parámetros de funciones. Esto hace que no necesitemos crear interfaces con un sólo método para crear callbacks o listeners, como ocurría en Java 7. Podemos hacer:


fun asyncTask(callback: (Boolean) -> Unit) {
  // Operación asíncrona
  callback(true)
}

Y la forma de usarla sería:


asyncTask {
  if (it) {
    print("Operación asíncrona realizada con éxito")
  }
}

Un par de consideraciones extra aquí:

  • Si el último parámetro de una función es a su vez una función, lo podemos sacar de los paréntesis. Y si los paréntesis se quedan vacíos, eliminarlos. Esto nos permite escribir estas estructuras de bloque que nos ayudan crear nuestros propios DSLs.

  • Si la función sólo tiene un parámetro de entrada, se puede usar la palabra reservada it.

La mezcla de funciones de extensión con lambdas lleva la expresividad de Kotlin a otro nivel. Imaginemos que tenemos un objeto Database que necesitamos abrir y cerrar cada vez que escribimos en él. Con Kotlin nos podríamos crear esta función de extensión:


interface Database {
    fun open()
    fun close()
    fun add()
}

inline fun Database.use(f: Database.() -> Unit) {
    open()
    f()
    close()
}

Con esto ahora podemos crear bloques donde previamente se abrirá la base de datos, y se cerrará una vez que se termine. Además, como la función que se pasa por parámetro es a su vez una función de extensión, podemos usar los métodos de Database como si estuviéramos dentro de la clase:


db.use {
    add("item1")
    add("item2")
}

Muchas más características

Por no hacer este artículo mucho más extenso, te cuento algunas otras características que podrás encontrar en Kotlin:

  • Las interfaces pueden tener código: de esta forma, puedes extraer código común a interfaces sin sentirte obligado a usar herencia. Eso sí, las interfaces no pueden tener estado, por lo que cualquier property que declares necesitará ser sobrescrita por quien la implemente para darle un valor.

  • Las colecciones pueden usar operaciones funcionales: aunque las colecciones de Kotlin son wrappers de las de Java para maximizar la compatibilidad con otras librerías, están supervitaminadas. Podrás hacer decenas de operaciones sobre ellas usando toda la potencialidad de las lambdas, como filter, sort, map, reverse y un largo etcétera.

  • Se pueden sobrescribir los operadores, de tal forma que tus clases pueden hacer uso de ellos de la forma que necesiten. No se pueden añadir más operadores que los definidos por defecto.

  • La delegación de propiedades es otra característica muy interesante, que permite delegar el valor de una propiedad a otra clase. Gracias a esto, podemos tener propiedades lazy, u obligar a que los setter tengan ciertas condiciones, o ejecutar una acción cuando el valor se modifique...

Kotlin For Android Developers

Conclusión

Si estás acostumbrado a trabajar con Java, sobre todo si te encuentras en entornos tan restrictivos en cuanto a versión de Java como es Android, encontrarás en Kotlin un gran aliado.

Kotlin es un lenguaje mucho más expresivo y legible, y ahorra gran cantidad de código innecesario. No te olvides de echarle un vistazo a la referencia oficial el lenguaje. Si trabajas en Android, también puede interesarte el libro que he escrito sobre el tema.

Empezar con Kotlin es muy fácil, simplemente descarga IntelliJ y crea un nuevo proyecto Kotlin. A partir de ahí podrás empezar a probar todo lo que he ido tratando en este artículo.

¿Qué te parece Kotlin? ¿Crees que es un lenguaje con posibilidad de hacer frente a los ya existentes en la JVM?

Comentarios cerrados
Inicio