Compartir esta publicación

En Apiumhub hacemos un uso intensivo de una arquitectura bautizada como Pure MVP (MVPP a partir de ahora), la cual se basa en los conceptos de composición de funciones e inversión de dependencias, y sobre la que podéis leer más en estos posts (MVPP en iOS, MVPP en Android).
En los siguientes puntos aquí expuestos se pretende comparar Kotlin vs Java, verémos cómo hacíamos las cosas antes de la llegada de Kotlin, y cómo hemos mejorado la legibilidad de nuestro código gracias a ellas.

Kotlin vs Java

Lambdas y funciones como parámetros

Para realizar el enlazado de los eventos de la vista con los del servicio, en lugar de exponer directamente el Observable para que la otra parte realice la suscripción, exponemos métodos que reciben funciones como parámetros; funciones que se ejecutarán cuando se produzca un evento bien en la vista o en el servicio, tal que así:


//KOTLIN
interface View {
  fun requestData(func: (isPaginating: Boolean) -> Unit)
  fun onDataLoaded(newData: Data)
}
interface Service {
  fun getData(isPaginating: Boolean)
  fun onDataLoaded(func: (data: Data) -> Unit)
}
class Presenter(view: View, service: Service) {
  init {
    view.requestData {
      service.getData(it)
    }
    service.onDataLoaded {
      view.onDataLoaded(it)
    }
  }
}

Una implementación similar en Java sería:


//JAVA
public interface View {
    void requestData(Consumer func);
    void onDataLoaded(Data newData);
}
public interface Service {
    void getData(boolean isPaginating);
    void onDataLoaded(Consumer func);
}
public class Presenter {
    Presenter(final View view, final Service service) {
        view.requestData(service::getData);
        service.onDataLoaded(view::onDataLoaded);
    }
}

La gran diferencia entre ambas implementaciones es el hecho de que en Java necesitamos hacer uso de la clase Consumer para pasar una función como parámetro (en este caso una función que recibe un parámetro de entrada de tipo Boolean y no devuelve nada). Si quisiéramos que esta función que pasamos como parámetro devolviera algo tendríamos que cambiar Consumer a Function, y si quisiéramos que no tuviera parámetro de entrada pero si de salida tendríamos que usar Supplier. Pero la cosa no termina aquí, si en vez de un parámetro quisiéramos dos tendríamos que usar BiFunction o BiConsumer, y si en lugar de dos son tres, Java no nos proporciona una solución como podríamos esperar (TriFunction o TriConsumer), si no que bien la construimos nosotros o bien usamos Function<T, Function<U, R>> o Function<Function<T,R>, U>. Cualquiera de las soluciones es definitivamente menos legible y más complicada de definir e implementar que la proporcionada por Kotlin, donde el paso de funciones como parámetro están integradas en el propio lenguaje.

  HTTPS para dummies: encriptación asimétrica, encriptación simétrica, certificado SSL

El operador .apply

Cuando tenemos que instanciar un Fragment, Android nos insta a hacerlo a través de un método estático en el cual construiremos un Bundle donde escribiremos los argumentos que queramos pasarle al Fragment. Esto es debido a que este Bundle es almacenado por el sistema operativo para poder reconstruir este Fragment en caso de que sea destruido.
En Java necesitaríamos hacer algo así:


static MyFragment newInstance(String arg1, String arg2) {
  MyFragment fragment = new MyFragment();
  Bundle arguments = new Bundle();
  arguments.putString(ARG_1_KEY, arg1);
  arguments.putString(ARG_2_KEY, arg2);
  fragment.setArguments(arguments);
  return fragment;
}

Mientras que en Kotlin, usando el operador .apply, haríamos algo así:


companion object {
  fun newInstance(arg1: String, arg2: String): MyFragment {
    return MyFragment().apply {
      arguments = Bundle().apply {
        putString(ARG_1_KEY, arg1)
        putString(ARG_2_KEY, arg2)
      }
    }
  }
}

Si bien no hay diferencia en el número de líneas entre los dos snippets, en el de Kotlin no es necesario guardar referencias al Fragment ni al Bundle, y además podemos acotar el scope de cada parte del código, ya que sabemos que dentro de cada .apply estamos escribiendo código en el que this hará referencia al objeto sobre el que hemos hecho el .apply

  • Modificador internal: Otro punto a favor de Kotlin respecto a Java es el modificador de visibilidad internal, el cual nos aporta visibilidad a una clase, método, interfaz, etc… acotada al módulo en el que se encuentra. Esto es especialmente interesante si nos interesa poner bajo test una funcionalidad que no queremos exponer fuera de nuestro módulo haciéndola pública.
  • Corrutinas: Otra de las grandes novedades que nos aporta Kotlin (aunque todavía es experimental en la versión 1.1) son las corrutinas. Una corrutina no es más que un bloque de código que se ejecuta de forma asíncrona respecto al thread desde el que ha sido invocado. Podemos pensar que para esto ya están los threads, pero hay varias diferencias.
    • Con un Thread, es el sistema operativo el encargado de gestionar la ejecución del mismo, suspenderlo, reanudarlo, cambiar de contexto, etc… Estas operaciones son pesadas y si intentamos lanzar un número elevado de threads, por ejemplo, un millón, nuestro procesador se colapsará y pasará más tiempo cambiando de un thread a otro que ejecutando el código que queremos que ejecute. Esto se debe a que un Thread que instanciemos en Java (o Kotlin) se corresponde con un Thread del sistema operativo (ya sea físico o virtual), y por lo tanto es el scheduler del sistema operativo el encargado de priorizar qué thread debe ejecutarse en cada momento.
    • Sin embargo, una corrutina no tiene una correspondencia con un thread del sistema operativo, si no que es el propio lenguaje (en este caso la JVM) el encargado de ejecutar cada una de las corrutinas y de cambiar entre ellas cuando sea necesario. En un ejemplo de la propia documentación de Kotlin podemos ver esta situación en la que se compara la instanciación de un millón de Threads y la de un millón de corrutinas:

val c = AtomicInteger()
for (i in 1..1_000_000)
    thread(start = true) {
        c.addAndGet(i)
    }
println(c.get())
val c = AtomicInteger()
for (i in 1..1_000_000)
    launch {
        c.addAndGet(i)
    }
println(c.get())

Los resultados que arroja el experimento es que es definitivamente más rápido usar corrutinas que threads, aunque si bien esta no es una comparación como tal ( Kotlin vs Java ), si que podemos comparar una funcionalidad ampliamente usada en Java (Threads) con una funcionalidad implementada en Kotlin(Corrutinas)

  Ventajas de React 18: guía completa

Estos son sólo algunos de los cambios que nos trae Kotlin respecto a Java y que hacen nuestra vida más sencilla a la hora de desarrollar aplicaciones para la JVM.

Suscríbete a nuestro newsletter para estar al día de Kotlin vs Java y desarrollo movil en general !

Si este artículo sobre Kotlin vs Java te gustó, te puede interesar:

Tendencias en aplicaciónes móviles

Patrón MVP en iOS

Debugging con Charles Proxy en Android emulator

Por qué Kotlin? 

Integración Continua en iOS usando Fastlane y Jenkins

Meetups de arquitectura de software 

MVPP en Android

Author

  • Diego Ojeda

    Android developer with over 8 years of experience on the platform. Passionate about software architecture, best practices, design patterns & teamwork.

    Ver todas las entradas

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Suscríbete a nuestro boletín de noticias

Recibe actualizaciones de los últimos descubrimientos tecnológicos

¿Tienes un proyecto desafiante?

Podemos trabajar juntos

apiumhub software development projects barcelona
Secured By miniOrange