Table of Contents
Si hoy en día me preguntaran cual es una de las características que distingue el desarrollo de aplicaciones Android del resto de campos, no dudaría en responder que la posibilidad de ejecutar la misma aplicación en dispositivos con un hardware tan diferente de forma nativa es una de ellas; pero… ¿cómo es esto posible? ¿ Por qué Kotlin ?
¿ Por qué Kotlin, Android ? ¿A qué se debe la elección de este lenguaje por parte de Google?
A estas alturas a nadie le sorprende ver funcionando la misma aplicación web en cualquier dispositivo y en cualquier plataforma (Android, iOS, Windows, MacOS…), todos sabemos que estas aplicaciones son más lentas e inestables que cualquier aplicación nativa; pero que a cambio sólo hemos de desarrollar una aplicación para todas las plataformas. Un problema similar se nos presentaría al hablar de la cantidad de dispositivos distintos en los que funciona Android ahora mismo; y digo se nos presentaría si no fuera por la existencia de Java. La potencia de Java y lo que lo ha hecho ser usando en miles de millones de dispositivos a día de hoy es su capacidad de funcionar en cualquier dispositivo independientemente de su hardware y su software, siempre y cuando cuente con un interprete del código pseudo-compilado que genera el compilador de Java (El intérprete oficial de Java es la Java Virtual Machine, aunque en Android se usaba Dalvik en las primeras versiones y ART a día de hoy).
¿Significa esto que Java es la solución a todos los males? Por desgracia, nada más lejos de la realidad… Aunque Java nos soluciona el problema de la interoperabilidad entre dispositivos, nos abre un nuevo abanico de quebraderos de cabeza que bien nos gustaría poder quitar de la ecuación, siendo algunos de ellos*:
* Nota: Muchos de estos problemas, aunque estén resueltos en Java 8 y 9, no están disponibles en el SDK de Android por debajo de API 24, lo que los hace prácticamente inutilizables)
- No hay soporte nativo para opcionales (aunque si lo tengamos para colecciones inmutables). Aunque existe la clase Optional<T>, su uso implica la generación de una gran cantidad de código boilerplate, el cual nos podríamos ahorrar si el soporte para los opcionales estuviera construido dentro del propio lenguaje y no como una extensión del mismo.
- No hay soporte nativo para la programación funcional: En Java existe la Stream Api (Una vez más sólo tiene soporte en Android a partir de API 24), pero su integración en el lenguaje es similar a la de Optional, existe de forma pobre en los objetos asociados a tipos primitivos (IntStream, DoubleStream…), y a través de una clase Stream<T> para el resto de objetos.
- Soporte para funciones anónimas (Lambdas). Aunque Java 8 incorpora soporte para funciones Lambda, estas no son ciudadanos de primera clase; esto quiere decir que, aunque podamos usar lambdas para implementar anónimamente interfaces con un sólo método, Java no soporta el paso de funciones como argumentos de una función.
Además, la falta de inferencia de tipos hace bastante incómoda la declaración de Lambdas, especialmente en el intento de simular funcionalidades como composición de funciones o currying, a falta de soporte a las mismas en el lenguaje.
- Nulabilidad de tipos: Aunque esto se puede englobar dentro del apartado referente a los opcionales, la dimensión de este problema merece una mención especial. ¿Cuántos programadores Java no han llenado su código de if (foo != null) para luchar contra la temida NullPointerException? (De hecho, su creador se disculpó por lo que el ha llamado el “error de los mil millones de dólares”) ¿Y cuántas de esas comprobaciones son algo más que parches para evitar un crash en nuestra aplicación?
- Binding de vistas manual: Aunque este problema sea específico de Android como Plataforma, y no de Java como lenguaje, también es de señalar la cantidad de código boilerplate necesario para obtener una referencia a una vista en Android. Aunque hayamos conseguido eliminar el odiado findViewById(int id) gracias a dataBinding, seguimos teniendo que almacenar una referencia a dicho binding.
- En un ámbito más general, pero no por ello menos importante, Java es un lenguaje muy verboso, requiere escribir una gran cantidad de código para cualquier operación, así como requiere generar una gran cantidad de ficheros (uno por clase). La primera problemática nos puede conducir a un código más costoso de mantener y más propenso a errores. La segunda a un problema de proliferación de clases.
¿Por qué Kotlin rompe con todo esto?
Es por todas estas razones que, a día de hoy, se considera Java como un lenguaje que, al menos en lo que al desarrollo de aplicaciones Android se refiere, no evoluciona a la velocidad a la que lo hace la industria.
Conforme pasa el tiempo se hace más imperiosa la necesidad de contar con un lenguaje con un soporte real y nativo para todo lo mencionado anteriormente, así como también es necesario mantener la principal característica de Android expuesta al principio de este artículo, su capacidad para, escribiendo y compilando una única aplicación, hacer que esta funcione en cualquier dispositivo y versión del mismo.
En esta dirección se han explorado muchas posibilidades, siendo algunas de ellas el uso de Swift o Scala, aunque ninguna ha sido muy prometedora.
Todo esto cambió con la aparición de Kotlin. Kotlin es un lenguaje diseñado y desarrollado por Jetbrains, enfocado a ser un lenguaje moderno, en constante evolución y, sobre todo, que pueda ser ejecutado sobre la JVM. Por qué Kotlin, empezaba a tener sentido para ser utilizado en Android.
Para empezar a demostrarlo, podemos contraponer todos los cons que sacamos a Java con Por qué Kotlin actua mejor frente a ellos:
-
- Opcionales: Están integrados en el lenguaje en Kotlin, basta con declarar el tipo de una variable terminando en un símbolo de interrogación ? Para que este sea opcional. Además nos ofrece la posibilidad de hacer unwrap seguros de estos opcionales listener?.onSuccess() sin la necesidad de comprobar si hay un valor para ese opcional, y nos proporciona el Elvis Operator.
-
- Programación funcional: En Kotlin encontramos soporte nativo para trabajar con colecciones y sets de datos como Streams. Podemos llamar directamente a .flatMap { } en una colección, así como .filter{ }, .map{ }, y un largo etcétera.
La inferencia de tipos hace especialmente manejable el uso de Lambdas. - Lambdas y funciones de orden superior: El punto anterior se completa con el hecho de que en Kotlin, las funciones son ciudadanos de primera clase. Podemos definir funciones que reciban otras funciones como parámetros. Un ejemplo de esto es la propia definición de la función map:
- Programación funcional: En Kotlin encontramos soporte nativo para trabajar con colecciones y sets de datos como Streams. Podemos llamar directamente a .flatMap { } en una colección, así como .filter{ }, .map{ }, y un largo etcétera.
inline fun <T, R> Iterable.map(transform: (T) -> R): List (source)
Aunque a primera vista este código pueda parecer un poco caótico, la parte que nos interesa es
transform: (T) -> R
Esto significa, la función map tiene un parámetro llamado transform, el cual es a su vez una función que tiene un parámetro de entrada de tipo T y devuelve un objeto de tipo R.
Gracias a este soporte nativo para lambdas, en Kotlin podemos usar la función map tal que:
collection.map { item -> aTransformation(item) }
Este snippet de código nos devolverá una colección de elementos del tipo que devuelva aTransformation.
-
- Nulabilidad de tipos: En Kotlin, al existir un soporte integrado en el lenguaje para opcionales, deberíamos tener el mínimo número posible de objetos nullables en nuestro código. Debido a existir un soporte integrado en el lenguaje para opcionales. Pero aún así, en caso de existir, Kotlin nos ofrece herramientas para que lidiar con ellos sea más sencillo que en Java. Por ejemplo contamos con el operador safe call (?.) para evitar NullPointerException al acceder a un opcional, o con el operador safe cast para protegernos en caso de querer realizar un casting.
El compilador de Kotlin, además, fuerza a controlar los tipos que podrían tener valor nulo, e incluso introduce comprobaciones runtime en caso de compatibilización con código Java. - Binding de vistas: Siendo este un problema específico de Android, Jetbrains nos ofrece Kotlin Android Extensions; una librería de soporte oficial para simplificarnos este problema (y algún otro) a través de un plugin de gradle.
- Verbosidad del lenguaje:
- Nulabilidad de tipos: En Kotlin, al existir un soporte integrado en el lenguaje para opcionales, deberíamos tener el mínimo número posible de objetos nullables en nuestro código. Debido a existir un soporte integrado en el lenguaje para opcionales. Pero aún así, en caso de existir, Kotlin nos ofrece herramientas para que lidiar con ellos sea más sencillo que en Java. Por ejemplo contamos con el operador safe call (?.) para evitar NullPointerException al acceder a un opcional, o con el operador safe cast para protegernos en caso de querer realizar un casting.
Java
public interface Listener {
void success(int result);
void error(Exception ex);
}
public void someMethod(Listener listener) {
int rand = new Random().nextInt();
if(listener != null) {
if (rand <= 0) {
listener.onError(new ValueNotSupportedException());
}
else {
listener.success(rand);
}
}
public void fun(Type1 param1) {
param1.someMethod(new Listener() {
@Override
public void success(int result) {
println(“Success” + result);
}
@Override
public void error(Exception ex) {
ex.printStackTrace();
}
}
}
Kotlin
fun someMethod(success: (Int) -> Unit, error: (Exception) -> Unit) {
val rand = Random().nextInt()
if (rand <= 0) {
error(ValueNotSupportedException())
else {
success(rand)
}
}
}
fun (param1: Type1) {
param1.someMethod( {
println(“Success” + it);
}, {
it.printStackTrace();
})
}
O incluso, usando expressions:
fun someMethod(success: (Int) -> Unit, error: (Exception) -> Unit) {
val rand = Random().nextInt()
if (rand <= 0) error(ValueNotSupportedException()) else success(rand)
}
fun (param1: Type1) {
param1.someMethod( {
println(“Success” + it);
}, {
it.printStackTrace();
})
}
Queda a disposición del lector decidir cuál de los dos snippets es más sencillo de escribir e interpreta.
Entonces, ¿ Por qué Kotlin ? Es gracias a todo lo expuesto anteriormente, junto con otra gran cantidad de características que no cabían en este artículo o que estaban fuera de lo que nos ocupa, que por qué Kotlin parece la apuesta más prometedora para los próximos años en el mundo del desarrollo mobile. En posteriores artículos estudiaremos más detalladamente qué nos aporta el uso de Kotlin en el desarrollo de aplicaciones Android y está siendo su impacto en la industria.
Este ha sido el primer artículo sobre ¿ Por qué Kotlin ? Estad atentos al blog para mas futuras discusiones.
Si este artículo sobre Por qué Kotlin te gustó, te puede interesar:
Principio de responsabilidad única
Tendencias en aplicaciónes móviles
Debugging con Charles Proxy en Android emulator
Suscríbete a nuestro newsletter para estar al día de los eventos, meet ups y demás artículos!
Author
-
Android developer with over 8 years of experience on the platform. Passionate about software architecture, best practices, design patterns & teamwork.
Ver todas las entradas
4 Comments
Leo
Pero en que momento de la practica real se podría usar programación funcional?
Diego Ojeda
Hola,
las aplicaciones de un paradigma de programación declarativa frente a uno imperativo hoy en día son infinitas dada la enorme cantidad de sistemas distribuidos o dispositivos donde se ejecutan un elevado número de tareas asíncronas.
Un ejemplo de esta aproximación muy utilizado a día de hoy es ReactiveX, una librería que bebe de los patrones Observer e Iterator y nos proporciona operadores para poder manipular los datos de múltiples formas.
Es por esto que, a pesar de que el concepto de programación funcional fue acuñado hace unas cuantas décadas, a día de hoy está en boca de toda el mundo.
Juan Pérez
Muy buen post. Pero vos sois feo como tu solo.
Armando Mtz
Comencé haciendo Apps en Swift y estoy por migrar a Android… fui demasiado feliz al investigar acerca de Kotlin y lo similar que es a swift, honestamente Java era una de las razones que le tenía miedo a programar para Android… veremos si Kotlin es un hijo de Swift, o viceversa…