Este artículo fue publicado originalmente en el blog oficial de Kotlin.
Estamos muy emocionados con el lanzamiento de Kotlin 1.1. Las nuevas características incluidas en esta versión son extremadamente útiles para los desarrolladores de Java y para llevar el desarrollo JVM a un nuevo mundo de posibilidades.
Pero estas nuevas características, como las coroutines, o los type aliases (por poner un par de ejemplos), parecen ciencia ficción para desarrolladores de Android.
Todavía estamos atrapados en el antiguo Java 6 con pequeñas mejoras que nos obligan a desarrollar de formas casi olvidadas para la mayoría de los desarrolladores de cualquier otra plataforma.
Por lo tanto, sería normal preguntarse: ¿el equipo de Kotlin ha sido capaz de mantener la compatibilidad con Java 6 mientras saca todas estas nuevas características? La respuesta es: ¡por supuesto!
Todas estas nuevas características están disponibles para Java 6 y, por extensión, para desarrolladores de Android. Y hoy, quiero mostrarte algunas de ellos, y cómo pueden hacer tu vida aún más fácil al desarrollar aplicaciones de Android.
Type aliases: puedes hacer que tu listener sea mucho más legible
Por supuesto, los type aliases tienen muchas aplicaciones diferentes. Pero lo primero en lo que pensé fue la capacidad de hacer que los listeners sean más legibles, sin dejar de usar lambdas.
Todas estas nuevas características están disponibles para Java 6 y, por extensión, para desarrolladores de Android
Si no has oído hablar antes de los type aliases, son básicamente una forma de cambiar el nombre de los tipos complejos a otros más legibles.
Por ejemplo, podrías tener un RecyclerViewadapter
que recibiera un listener. Como sabrás, RecyclerView
no tiene una forma estándar de tratar los clicks en los items, como pasa en un ListView
, por lo que tenemos que salir al paso usando los que hayamos creado.
Vamos a imaginar que queremos un listener que tenga acceso a la vista. Nuestra clase adapter podría ser parecida a esta:
class MyAdapter(val items: List- , val listener: (View) -> Unit) : RecyclerView.Adapter
() {
...
}
Y tu ViewHolder probablemente también tendría que recibir ese listener, para asignarlo a la vista:
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(item: Item, listener: (View) -> Unit) {
itemView.setOnClickListener(listener)
}
}
Este no es un caso muy complicado, pero como puedes ver, necesitamos repetir la definición de la lambda y eso, fuera de contexto, puede dificultar la lectura.
Pero podemos crear un type alias que represente un click listener:
typealias ClickListener = (View) -> Unit
Y luego utilizarlo en todos los lugares que necesitamos el listener:
class MyAdapter(val items: List- , val listener: ClickListener)
o
fun bind(item: Item, listener: ClickListener) { ... }
Las data classes ahora son más potentes
Las data classes son geniales porque evitan una gran cantidad de código boilerplate. Pero carecían de cierta potencia, que las hacía inútiles en algunos casos.
Una de las nuevas características de Kotlin 1.1 es la herencia: las data classes ahora pueden heredar de otras clases. Esto permite que las data classes sean parte de las clases sealed:
sealed class UiOp {
object Show : UiOp()
object Hide : UiOp()
data class Translate(val axis: Axis, val amount: Int): UiOp()
}
Y ahora, como las clases sealed pueden ser definidas fuera de la clase padre, esto también podría escribirse así:
sealed class UiOp
object Show : UiOp()
object Hide : UiOp()
data class Translate(val axis: Axis, val amount: Int): UiOp()
Desestructuración en lambdas
Las data classes pueden ser desestructuradas desde la primera versión gracias a los métodos componentX()
que generan. Podrías distribuir el contenido de una data class en varias variables de esta manera:
data class Item(val text: String, val url: String)
val (text, url) = item
Pero faltaba una característica realmente potente: Poder hacer esto en lambdas. ¡La espera ha terminado! Ahora puedes hacer algo como esto:
fun bind(item: Item) = item.let { (text, url) ->
textView.text = text
imageView.loadUrl(url)
}
También es muy útil para usarlo en pares o conjuntos de clave/valor en un mapa de datos, por ejemplo.
Delegación de propiedades locales
La delegación de propiedades ha demostrado ser realmente útil para proporcionar habilidades extra a las propiedades de nuestras clases.
Por ejemplo, uno de los casos más útiles es la delegación perezosa (lazy delegation), que retrasa la asignación hasta que la propiedad se utiliza por primera vez.
Pero lazy también podría ser muy útil también en las variables, y Kotlin carecía de esta característica.
Ahora, con la delegación de propiedades locales, podemos hacerlo:
fun testLocalDelegation() {
val database by lazy { createDatabase() }
val cache by lazy { createMemoryCache() }
if (mustUseDatabase()) {
database.use { ... }
} else {
cache.use { ... }
}
}
Aunque este ejemplo puede resolverse sin usar la delegación lazy, ayuda a entender el concepto.
la delegación *lazy* retrasa la asignación hasta que la propiedad se utiliza por primera vez
Tenemos un par de objetos pesados que pueden usarse o no. Mediante el uso de lazy, podemos retrasar la instancia hasta que estamos seguros de que vamos a utilizarlo.
La primera vez que se utilice, el código dentro de las llaves se ejecutará, y se almacenará en caché en caso de que se necesite de nuevo más tarde.
Olvídate de declarar las variables no utilizadas en lambdas
Era muy común tener que declarar variables para argumentos de lambdas que al final no se usaban en ninguna parte.
Se debía a que en Kotlin 1.0 no teníamos una forma de descartar parámetros que no utilizáramos.
Como ejemplo, en el artículo donde expliqué cómo actualizar el adapter de RecyclerView usando delegación, terminé con este código:
var items: List by Delegates.observable(emptyList()) {
prop, old, new ->
autoNotify(old, new) { o, n -> o.id == n.id }
}
El atributo prop se utilizaba pero, hasta ahora, era necesario declararlo. Ahora puedes evitar incluirlo utilizando un guión bajo:
var items: List by Delegates.observable(emptyList()) {
_, old, new ->
autoNotify(old, new) { o, n -> o.id == n.id }
}
Pero era peor el caso en el que no se usaba ninguno de ellos. Si tienes más de un argumento para una lambda, debes escribirlos todos aunque no los uses.
Ahora podemos ignorarlos:
var items: List- by Delegates.observable(emptyList()) {
_, _, _ ->
notifyDataSetChanged()
}
No sólo se están definiendo menos variables, sino que también el código se vuelve más legible. No es necesario detectar si esos argumentos se utilizan o no. Queda mucho más claro.
Coroutines
Las coroutines son las mejores noticias que hay en Kotlin 1.1. Aunque finalmente se dejaron como experimentales en este lanzamiento, son completamente funcionales y puedes comenzar a utilizarlas en tus proyectos desde hoy.
Las coroutines te permitirán escribir código asíncrono de manera síncrona, lo que permite suspender su ejecución en algún momento y esperar a un resultado, todo mientras se escribe código secuencial.
Una cosa que sabrás sobre las coroutines en Kotlin es que no son una biblioteca o una implementación específica, son una característica del lenguaje que permite crear las bibliotecas sobre ella.
Las coroutines en Kotlin son completamente funcionales y puedes comenzar a utilizarlas en tus proyectos desde hoy
Por lo tanto, aunque el código resultante puede parecer similar, es importante saber cuál es el "engranaje" que están creando esos hilos secundarios y volver al hilo principal, lo que es muy importante en Android.
Afortunadamente, la comunidad de Kotlin se mueve rápido y ya hay varias bibliotecas que traen la potencia de las coroutines a Android. Aquí tienes algunos ejemplos:
Los primeros que te pueden interesar son los oficiales que ofrece Jetbrains:
- Kotlinx-coroutines-android, que proporciona una implementación de las coroutines lista para ser usada en Android.
- Anko, que en su última versión beta incluye compatibilidad con coroutines para muchos listeners del framework.
Pero también hay muchas otras bibliotecas de terceros que implementan sus propias versiones de las coroutines:
- AsyncAwait-Android de Niek Haarman
- Async / Await de Metalab
- Si sólo quieres soporte para Retrofit, puedes echar un ojo a kotlin-coroutines-retrofit por Andrey Mischenko
Te animo no sólo a usarlas, sino también a comprobar cómo se implementan. Es la magia del open source.
Algunas otras cosas interesantes para desarrolladores Android
Hay muchas otras mejoras en esta versión, pero quiero resaltar algunas que están más centradas en el desarrollo de Android.
La primera de ellas es que ahora puede habilitar el soporte con el compilador Jack usando: jackOptions {true}
. Google ha anunciado que está deprecando las herramientas de Jack, pero si las utilizas para Java 8, esto puede serte útil hasta que se publique la versión final de Android Studio 2.4.
Además, existe una nueva intention que utilizará @JvmOverloads
para implementar los constructores de vistas personalizadas, que permite, literalmente, implementar constructores para vistas personalizadas en una línea (bueno, una línea muy larga) utilizando un constructor y valores predeterminados para los argumentos:
class CustomView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
...
}
Conclusión
Kotlin 1.1 ha venido con un buen montón de nuevas características impresionantes que hacen que dejar de seguir utilizando Java sea aún más inevitable.
La potencia que Kotlin trae a los desarrolladores de Android está fuera de toda duda, y puedes comenzar a escribir sus aplicaciones de Android en Kotlin a partir de ahora.
Y si quieres aprender Kotlin para Android desde cero mientras desarrollas una aplicación, puedes apuntarte a este vídeo-training gratuito sobre Kotlin para desarrolladores Android que lancé hace unos días.
- Artículo original | Kotlin 1.1 is also for Android Developers
- En Genbeta Dev | ¿Será 2017 el año de Kotlin? Repasamos su evolución y por qué deberías darle una oportunidad