Introducción a Kotlin para el desarrollo de apps en Android

3
9680

En este tutorial se habla de Kotlin, un lenguaje que funciona sobre la JVM, como alternativa Java

Índice de contenidos

¿Qué es Kotlin?

Kotlin es un lenguaje que funciona sobre la JVM, es una alternativa moderna a Java priorizando la compatibilidad con Java. Intenta solucionar los problemas más comunes de Java cómo pueden ser las Null Pointer Exception o la verbosidad.
Algunas de las características más destacadas de Kotlin son:

  • Fuerte inferencia de tipos, sobre todo trabajando con genéricos.
  • Protección frente a null, los tipos por defecto no pueden ser null, en caso de que un tipo venga de un API de Java, Kotlin te obliga a tener en cuenta ese null.
  • Características de programación funcional, cómo higher order functions, Pattern Matching y otras funcionalidades provenientes de lenguajes funcionales.

Kotlin y Android

Google eligió Java cómo lenguaje para Android, a pesar de que Java ha seguido evolucionando desde ese momento, en Android se sigue utilizando Java 6 (aunque con algunas características de Java 7), a estas versiones se les empieza a notar la edad, teniendo en cuenta todos los avances que han ido habiendo en el diseño de lenguajes, plasmado en algunos cómo Scala o Swift.

Kotlin soluciona muchos de los problemas que tiene Java sin añadir demasiada complejidad, es un lenguaje pequeño que puede usar todas las librerías que ofrece la JVM sin problemas, por lo que no se convierte en una apuesta arriesgada.

Todo ello sin perder la genial integración con Android Studio que ya conocemos con Java.

Además, la naturaleza de las aplicaciones Android, las cuales están bastante ligadas a la capa de presentación, las hace perfectas para un lenguaje cómo Kotlin, el cual es mucho más dinámico que Java.

Algo a tener en cuenta también es la posibilidad de mezclar clases Java y Kotlin en el mismo proyecto, de manera que migrar proyectos ya existentes a Kotlin no debería ser una tarea titánica sino algo progresivo.

Añadir Kotlin a un proyecto Android

Voy a dar por hecho de que el proyecto ya está usando Gradle, si no lo hace deberías migrarlo antes de nada

Para añadir Kotlin a nuestro proyecto, es tan fácil cómo añadir el plugin de Kotlin para Gradle, el cual se encargará de compilar el código de Kotlin de manera que pueda ser ejecutado en Android sin problema y añadir la dependencia de la librería standard de Kotlin.

En el build.gradle de nuestro proyecto "padre":

buildscript {
  ext.support_version = '22.2.1'
  ext.kotlin_version = '0.12.1218'
  ext.anko_version = '0.6.3-15s'
  repositories {
      jcenter()
  }
  dependencies {
      classpath 'com.android.tools.build:gradle:1.3.0'
      classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
  }
}

allprojects {
  repositories {
      jcenter()
  }
}

Y el build.gradle del proyecto de aplicación:

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'

android {
    compileSdkVersion 22
    buildToolsVersion "22.0.1"
    defaultConfig {
        ...
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    compile "org.jetbrains.anko:anko:$anko_version"
    ...
}

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
    }
}

Hemos añadido también la librería Anko, esta librería escrita en Kotlin, nos ofrece una serie de funciones de ayuda para agilizar el desarrollo en Android usando Kotlin, estas son totalmente opcionales pero no serán de gran ayuda.

El siguiente paso sería crear un fichero .kt para comprobar que todo se ha instalado correctamente, para ello en el mismo paquete donde tengamos nuestras clases creamos un fichero de utilidades que nos ayudará con el manejo de trazas.

import android.util.Log

public val LOG_ENABLED: Boolean = true;
public val TAG: String = "HNNotify"

public fun log(text: String) {
    if (LOG_ENABLED) {
        Log.d(TAG, text)
    }
} 

Una vez creada, podemos probar en cualquier actividad si se está ejecutando correctamente, para ello debemos importar el paquete.

import static com.danieldisu.utils.UtilsPackage.*;

public fun logIfItWorks() {
    log("Hello World");
}

Por convención Kotlin crea un paquete usando el nombre del paquete y la palabra "Package"

Porqué usar Kotlin en Android

A continuación voy a repasar algunas de las cosas que más me han gustado de usar Kotlin en Android, que suelen ir de la mano con los mayores puntos de verbosidad de Java.

Lambdas

Gracias a Java 8 las lambdas se han hecho conocidas en el mundo Java, aunque ya las usábamos de una manera u otra en Java desde bastante antes, por ejemplo, al añadir un OnClickListener a un botón en Android, extendíamos la clase abstracta y sobrescribíamos el único método de esta clase, el cual se llamará en el momento en el que el usuario pulse el botón. En otros muchos lenguajes cómo Javascript, esto se soluciona pasando una función por parámetro e invocando está función en el momento adecuado, Kotlin nos permite hacer esto mismo.

    
button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        log("User clicked!");
    }
});
  

En kotlin sería:

button.onClick { onUpdateButtonClick(it) }

o sin abreviar:

updateButton.onClick { view -> onUpdateButtonClick(view)}

El método onClick lo añade la librería Anko, al final solo es una manera corta de llamar al método setOnClickListener, en cuanto a la invocación, al tener un solo parámetro podemos usarlo con la keyword it.

Esto es posible gracias al siguiente punto:

Extender clases del sistema o librerías.

Kotlin nos permite añadir métodos/funciones a clases y librerías externas, cómo pueden ser las clases de Android, lo que nos permite hacer métodos de utilidad que podrán ser invocados con notación infija, lo cual nos dará la sensación de que los métodos pertenecen a la clase, además de permitir al IDE ofrecernos estos métodos cómo un método más al escribir el punto tras una variable.

public fun android.view.View.onClick(l: (v: android.view.View) -> Unit): Unit = setOnClickListener(l)

Esta es la función que nos extiende la clase View de Android permitiéndonos invocar al método onClick para añadir un listener.

Estas extensiones tienen limitaciones, ya que se resuelven de manera externa, no pueden acceder a ningún atributo privado de la clase, pero sí a otros métodos no estáticos.

Null safety

Kotlin es un lenguaje que ha sido creado para solucionar los problemas más comunes en Java, uno de ellos y quizás el que más problemas crea de manera indirecta es la existencia de nulos. Para ello Kotlin no permite inicializar ninguna variable a null a no ser que declaremos esa variable cómo nullable, es decir declarando explícitamente en el sistema de tipos que esa variable puede contener null. Además, obliga que al usar cualquier variable declarada cómo nullable tengamos en cuenta la posibilidad de que esta puede ser null.

Vamos a ver cómo es esto en la práctica:

  public var nullableString: String? = null 
  public var nonNullableString: String = null 
  public var nonNullableString: String 

En este caso solo la primera línea será válida, la segunda y tercera producirán un error de compilación.

  
fun functionThatUsesString(string: String): Unit {
  log(string)
}

fun main(){
  functionThatUsesString(nullableString)
}

Esto producirá un error de compilación, ya que functionThatUsesString necesita un String y nullableString es de tipo String?, para evitar esto podemos hacerlo de varias maneras:

var nullableString: String? = null;

if (nullableString != null)
  functionThatUsesString(nullableString)
else
  doSomethingElse()

Hacemos exactamente lo mismo que haríamos en Java, pero esta vez obligados por el compilador, lo cual nos ofrece la garantía de que se está teniendo en cuenta.

var nullableString: String? = null;

functionThatUsesString(nullableString ?: "otherString")

Usando el operador ?: que nos permite proponer una alternativa en caso de que este sea null.

var nullableString: String? = null;

functionThatUsesString(nullableString!!)

Si estamos completamente seguros de que no va a ser null podemos usar el operador !! que nos ofrece Kotlin, en caso de que si fuese null lanzará un Null Pointer Exception, por ello este método no es el más aconsejado.

Data Classes

Muchas veces creamos clases que no son más que contenedores de información, en Java una sencilla clase con 3 atributos nos obligaría a hacer un total de 8 métodos (getters , setters, equals y hashcode), podemos definirlas en una sola línea usando la anotación data sobre una clase:

public class Person {

  private String name;
  private String surname;
  private int age;

  public Person(String name, String surname, int age) {
      this.name = name;
      this.surname = surname;
      this.age = age;
  }

  public String getName() {
      return name;
  }

  public void setName(String name) {
      this.name = name;
  }

  public String getSurname() {
      return surname;
  }

  public void setSurname(String surname) {
      this.surname = surname;
  }

  public int getAge() {
      return age;
  }

  public void setAge(int age) {
      this.age = age;
  }

  @Override
  public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;

      Person person = (Person) o;

      if (age != person.age) return false;
      if (name != null ? !name.equals(person.name) : person.name != null) return false;
      return !(surname != null ? !surname.equals(person.surname) : person.surname != null);

  }

  @Override
  public int hashCode() {
      int result = name != null ? name.hashCode() : 0;
      result = 31 * result + (surname != null ? surname.hashCode() : 0);
      result = 31 * result + age;
      return result;
  }
}

Tanto los getter/setter cómo equals y hashCode han sido auto generados por AndroidStudio

VS la versión de Kotlin:

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

When Expression

When es un paso intermedio entre el clásico Switch y match (Pattern Matching) en Scala y otros lenguajes funcionales. Básicamente lo que nos permite es hacer algo cuando una de las condiciones se cumpla:

Se puede usar directamente con valores:

when (x) {
  1 -> print("x == 1")
  2 -> print("x == 2")
  else -> { // Note the block
    print("x is neither 1 nor 2")
  }
}

Con valores que se obtienen tras invocar un método:

when {
  x.isOdd() -> print("x is odd")
  x.isEven() -> print("x is even")
  else -> print("x is funny")
}

O con tipos:

when (x) {
  is String -> println("isString!")
  is StringBuffer -> println("isStringBuffer!")
  else -> println("isOtherThing")
}

Interoperabilidad con Java

Uno de los principales aspectos de diseño de Kotlin es mantener la interoperabilidad al máximo con Java, esto nos permite usar la gran mayoría de las librerías ya existentes sin ningún tipo de problema.

En este sencillo ejemplo estamos usando Retrofit, Gson, RxJava sin ningún tipo de problema:

public interface HNService {

    GET("/item/{itemId}.json")
    fun getItem(Path("itemId") itemId: String): Observable<HNItem>

}

object HNClient {

    private var client: HNService? = null

    fun get(): HNService {
        return this.client ?: createNewClient();
    }

    private fun createNewClient(): HNService {
        val restAdapter = RestAdapter.Builder()
                .setEndpoint("https://hacker-news.firebaseio.com/v0")
                .build();
        val hnClient = restAdapter.create(javaClass<HNService>())
        this.client = hnClient
        return hnClient;
    }
}

data class HNItem(val by: String,
              val descendants: Int,
              val id: Int,
              val kids: Array<Int>,
              val score: Int,
              val time: Long,
              val title: String,
              val type: String,
              val url: String) {
}

Conclusiones

Hay gente que opinará que esto es solo syntax sugar y que quizás no sea necesario, pero cómo dijo Andrey Breslav (dev lead de Kotlin) en una charla, "todos los lenguajes son syntax sugar sobre código máquina" , por ello creo que debemos dejarnos ayudar por este tipo de avances, que ayudan no solo a la hora de desarrollar si no a la hora de leer y mantener el código.

Kotlin además es un paso intermedio entre Java y Scala por lo que lo hace un lenguaje más atractivo para aquellos que no necesiten (o quieran) un lenguaje tan potente como Scala, además de ser bastante más pragmático y menos académico.

3 COMENTARIOS

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad