Kotlin. Why not?

POR CÉSAR DOMÍNGUEZ / STAFF MOBILE APPS DEVELOPER EN HTECH
2017: El bitcoin llegaba y superaba los 7,000 dólares, el presidente de México era Enrique Peña Nieto y las Chivas ganaban los campeonatos, tanto de la copa, como de la liga. En el mundo de la programación, Google anunciaba a Kotlin como lenguaje oficialmente soportado para el desarrollo de Android. ¡Vaya año!

Kotlin fue lanzado en 2011 por el equipo de la empresa JetBrains. Su ascenso ocurrió en un tiempo relativamente corto, en comparación con otros lenguajes, como Java, que también fue el lenguaje oficial inicial para el desarrollo de Android. A continuación, te diremos por qué ha ganado popularidad, así como algunos de sus beneficios:

En primer lugar, Kotlin es interoperable con la Máquina Virtual Java (JVM, por sus siglas en inglés) y con Java. Es decir, podemos mandar llamar al código de Kotlin desde un código en Java y viceversa: en pocas palabras, es una cross-platform con tipado estático. Por eso, empezar a migrar aplicaciones realizadas en Java a Kotlin es sumamente sencillo. De hecho, esa fue una de las razones de su buena adopción por parte de los equipos de desarrollo.

Según datos del sitio Developer, más del 60% de los desarrolladores usamos Kotlin en nuestras apps, además de que, dentro de las 1,000 apps más populares en Android, el 80% contienen Kotlin, sin mencionar que hasta el mismo Google usa Kotlin en sus apps.

Pero entremos más a detalle explicando algunos de los beneficios de usar Kotlin y, por qué no, veamos algunos ejemplos de código que, estoy seguro, te ayudarán a decidirte si vale la pena probar el lenguaje o desarrollar tu primera app Android con Kotlin.

¡Bye POJOS! ¡Hola DataClass!

Cuando deseamos trabajar con objetos en Java para modelar los datos que la aplicación manejará deberemos hacer algo similar a:

public class User {
    private long id;
    private String name;
    private String lastName;

    public User() {
        
    }

    public User(long id, String name, String lastName) {
        this.id = id;
        this.name = name;
        this.lastName = lastName;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
}

Si has usado Java verás que cuando queremos obtener cada atributo de nuestro objeto User y asignarle el valor debemos crear dos funciones, sin mencionar los dos tipos de constructores, dependiendo de la lógica de negocio. Ahora veamos cómo sería crear nuestro mismo objeto utilizando Kotlin:

data class User(val id : Long, val name : String, val lastName : String)

Es tan sencillo como agregar en una sola línea nuestro atributo, el cual podemos asignar y así obtener al instanciar nuestro objeto. Simple y hermoso.

NullPointerException

NullPointerException es un famosísimo error. Seguramente te ha ocurrido que cuando estás desarrollando en Java, al momento de probar nuestro código la consola te ha lanzado error, ocasionando, incluso, que nuestra app se cierre y, por ende, genere una mala experiencia a nuestros usuarios 😥.

Esto se debe a que simplemente Java no maneja los valores nulos de la mejor manera. Veamos nuestro ejemplo:

public class SampleTest {

    @Test
    public void Main() {

        User user = new User();

        System.out.println("The name of the user is " + user.getName());

        Assert.assertNotNull(user.getName());
    }
}

En el siguiente ejemplo, creamos un objeto tipo “user” y deseamos mostrar en la consola el nombre del usuario, sin embargo, al nunca asignarlo en nuestra consola regresará un “null” (valor nulo). Ahora imagínate que no lo queremos mostrar en la consola, sino en la pantalla de perfil de nuestra app, así que si no tenemos cuidado la aplicación no sabrá cómo manejar el valor, lo que ocasionará la excepción y, si no fuimos cuidadosos posiblemente se cerraría. Claro, con experiencia en Java y buenas prácticas podemos evitar caer en estos errores, pero, si nuestro lenguaje es capaz de ayudarnos con esto, ¡aprovechemos! Veamos cómo es el manejo de valores nulos en Kotlin.

@Test
fun Main() {
    
    val user = User(id =  0, name = "Jose", lastName = "Madero")

    println("The name of user is ${user.name}")
    
    Assert.assertNotNull(user.name)
}

Analicemos un poco el código: nuestro DataClass no nos permite crear instancias hasta que asignemos al menos un valor a cada atributo. Si no lo hacemos, nuestro IDE nos marcará un error y no podremos ni compilar nuestra app; ésta es una manera segura que Kotlin tiene para evitar error de nulos. Ahora veamos otro ejemplo donde nuestro DataClass sí nos permite tener valores nulos:

data class UserWithNulls(
    val name : String? = null
)

@Test
fun Main2(){
    
    val otherUser = UserWithNulls()

    println("The length name of user is ${otherUser.name?.length}")
}

En el ejemplo anterior creamos un nuevo objeto de tipo UserWithNulls, que es un DataClass que acepta que el nombre pueda ser un valor nulo. Al momento de instanciar el objeto, Kotlin no nos solicita un nombre debido a que por default es nulo y, al calcular la longitud del nombre para mostrarla en la consola Kotlin, nos ayuda a manejar el caso de uso cuando el nombre sea nulo mediante el símbolo “?”. Para que no caigamos en la excepción, mediante el símbolo “?” podremos manipular toda variable y/o atributo que hayamos indicado que pueda ser nula sin preocuparnos. Además, con el operador “Elvis” tenemos un mejor control sobre la acción que se debe hacer en caso de no tener valor nuestra variable:

println("The length name of user is ${otherUser.name?.length ?: 0}")

Como podemos ver, en este ejemplo, usando el operador “Elvis” podremos decidir si mostramos el valor 0 al no contar con un valor para el atributo “name”.

Async vs Suspend

Como bien sabemos, Java es multihilo, y la mayoría de nuestras apps necesitaran obtener datos de servicios REST o base de datos, o realizar tareas de larga duración o procesos, y una regla de oro en Android es que no podemos realizar estos procesos en nuestro hilo principal (el hilo de la UI), por lo que debemos crear tareas asíncronas o de segundo plano para estos casos. Entonces, veamos cómo sería ejecutar una tarea y cómo obtener información del usuario desde un servicio REST (simplificando la parte de la implementación del servicio REST).

Utilizando las AsyncTask de Java tenemos:

public class GetUserByIdAsyncTask extends AsyncTask<Void, Integer, User> {

    private UserRepository userRepository;

    private long userId;

    public GetUserByIdAsyncTask(long userId, UserRepository userRepository) {
        this.userId = userId;
        this.userRepository = userRepository;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();

        //Todo mostrar control de carga, preparar app para tarea
    }

    @Override
    protected User doInBackground(Void... voids) {
        return userRepository.findUserById(userId);
    }

    @Override
    protected void onPostExecute(User user) {
        super.onPostExecute(user);

        //Todo usuario obtenido mostrar en la UI
    }
}

Creamos nuestra clase GetUserByIdAsyncTask, que se extiende de AsyncTask para poder utilizar la función doInBackground, que es donde se ejecutaría el proceso de larga duración que no debemos ejecutar en el UI thread para no ocasionar un ANR (Android Not Respond). En este caso es una llamada a nuestro userRepository que simula la llamada a nuestro backend para devolvernos el “User”. Adicional a esto, tenemos el método on PostExecute, que estará recibiendo el objeto “User” para hacer con él lo que se necesite (almacenarlo, mostrarlo en la UI, etc.). La ejecución de esta tarea sería de la siguiente manera:

@Test
public void Main3(){

    long userIdToFind = 1;

    new GetUserByIdAsyncTask(userIdToFind, new UserRepository()).execute();
}

Es algo tedioso construir funciones para nuestros procesos asíncronos usando las AsyncTask. Por ello, ahora te mostraré una de mis funciones favoritas en Kotlin, que son las corrutinas.
En palabras prácticas, las corrutinas son APIS que nos permiten escribir código sumamente sencillo para tareas asíncronas. Veamos el equivalente de lo que hicimos en Java usando corrutinas en Kotlin:

suspend fun GetUserByIdAsyncTask(userId: Long, userRepository : UserRepositoy) : UserInKotlin = userRepository.findUserById(userId)

Al agregar la palabra “suspend” a una función automáticamente le indicamos a Kotlin que es una función que debe ejecutarse en otro proceso fuera del hilo principal. Siguiendo el ejemplo, nuestra clase userRepository es la que se encarga de tener la implementación de la llamada al backend; entonces, Kotlin ahora sabe que GetUserByIdAsyncTask debe ejecutarse en una corrutina, pero ¿cómo se hace? De la siguiente manera:

@Test
fun Main3(){

    val userId : Long = 1

    GlobalScope.launch {

    //Todo mostrar control de carga, preparar app para tarea
    
    val user = GetUserByIdAsyncTask(userId, UserRepositoy())

    //Todo usuario obtenido mostrar en la UI, el usuario se guarda en la variable user
}

}

Debemos crear un “scope” y dentro del mismo se ejecutará nuestra función de suspensión. De esta manera es muchísimo más legible implementar tareas en segundo plano. Como nota, todo el código que esté dentro del alcance del scope, es decir dentro de los paréntesis del GlobalScope.lauch, se ejecutará secuencialmente respetando las funciones de suspensión hasta que éstas hayan finalizado. En otras palabras, si tuviéramos más código después de la variable “User”, no se ejecutará hasta que la tarea GetUserByIdAsyncTask haya finalizado.

Estas son algunas de las comparaciones más destacadas que hay para desarrollar apps usando Java o usando Kotlin. En Htech creemos que la tecnología no lo es todo, ni que debemos aferrarnos a una u otra, simplemente es preciso evaluar cuál nos conviene a todo el equipo, al negocio y a los requisitos.

Así, crear apps con increíbles experiencias cada vez es más desafiante y qué mejor que aprovechar los beneficios de cada lenguaje para agilizar el desarrollo.

Por cierto, Kotlin ofrece más APIs increíbles para Android que te invito a seguir explorando en el portal de este lenguaje de programación.