Como hemos visto en artículos anteriores, las propiedades necesitan un valor por defecto, no se pueden declarar sin asignarles un valor.
Esto es un problema, porque imagina que quieres almacenar una vista en una property. Como este código es ejecuta durante la creación del objeto, no puedes acceder al contexto en ese momento.
¿Qué puedes hacer?
Si quieres aprender cuál es la situación de Kotlin en el mercado, por qué debes aprenderlo y los primeros pasos para que veas lo fácil que es, he preparado un training gratuito de hora y media al que puedes unirte haciendo click aquí
Delegación de propiedades: delega el valor de una property en otro objeto
La delegación de propiedades utilizará otro objeto que es capaz de devolver un resultado cuando se llame al get
y al set
(en caso de que se utilice var
).
En un mundo en el que no tenemos control sobre la creación de muchos objetos, como es el framework de Android, esta delegación nos va a salvar la vida en muchos casos.
Te voy a mostrar tres ejemplos que a mí me resultan muy útiles en Android
Setear el valor de una vista en una property
Para esto tenemos dos opciones utilizando delegación, y evitando usar null
, (algo muy poco recomendado).
La primera me gusta menos, porque obliga a utilizar var
para una propiedad que podría ser inmutable, y además es menos seguro.
Con la palabra reservada lateinit
le decimos a esa property que no va a estar vacía, pero que todavía no tenemos su valor final:
lateinit var textView:TextView
En el onCreate
podemos entonces asignarle el valor definitivo:
setContentView(R.layout.activity_main) textView = findView(R.id.welcomeMessage) toast(textView.text)
Esto en realidad no es un delegado, aunque hace la misma operación que el delegado notNull
, que quedó relegado en favor de este primero.
La segunda opción es mucho más elegante. Consiste en usar el delegado lazy
, que no ejecutará el código que se le indique hasta que no se llame por primera vez a la property:
val textView by lazy { findView(R.id.welcomeMessage) }
El findView
no se ejecutará hasta que no se llame al get
del textView
por primera vez. Es más seguro porque no se puede modificar el valor por error, y no nos obliga a acordarnos de setearlo tras el setContentView
.
En el momento en que hagamos:
toast(textView.text)
Se ejecutará el código de forma lazy.
Como ves, la forma de delegar es utilizando la palabra reservada by
.
Vamos a ver otro ejemplo
Notificar cambios en un adapter
En un adapter podemos tener una property items
que cada vez que se setee lance automáticamente una actualización del adapter
:
var items: List by Delegates.observable(emptyList()) { p, old, new -> notifyDataSetChanged() }
Simplemente establece un valor inicial, y posteriormente llama a la función que se define tras cada modificación.
En este caso solo estoy llamando a notifyDataSetChanged
, pero como ves, la función recibe los valores antiguos y nuevos, así que técnicamente podrías comprobar cuáles son los cambios y actualizar sólo la diferencia.
Declarar el grafo de Dagger de forma lazy
Es otra de la situaciones en las que he encontrado esta funcionalidad muy útil.
Volviendo a lazy, lo puedes utilizar para declarar el component
de la aplicación durante la declaración de la property:
val component: AppComponent by lazy { DaggerAppComponent .builder() .appModule(AppModule(this)) .build() }
De esta forma no necesitas utilizar lateinit
, y la propiedad se vuelve inmutable.
Puedes hacer lo mismo si estás utilizando subcomponent
s en las actividades:
class HomeActivity : AppCompatActivity(), HomePresenter.View { val component by lazy { app.component.plus(HomeModule(this)) } ... }
Conclusión
La declaración de propiedades te ayudará a hacer las propiedades mucho más potentes y simplificar y reutilizar código.
Aquí sólo hemos visto propiedades estándar de la librería de Kotlin, pero puedes crear las tuyas propias.
Por ejemplo en el libro tengo una implementada que almacena y recupera datos de las SharedPreference
.
¿Te gustaría comenzar hoy a dar el siguiente paso? Te recomiendo que entres en mi training gratuito aquí.
Hola!
Realmente encuentro muy útiles estas funciones, porque si me he topado muchas veces con la limitación de no poder inicializar un final en Java.
Sólo tengo algunas dudas:
¿Las properties val no pueden usar lateinit?
¿Cómo sabes a qué se refiere cada parámetro en las lambdas que recibes? Cómo en el adapter, no se bien para que es p, y así podría pasar en otras funciones.
Gracias
– los val no pueden usar lateinit
– Leyendo la documentación. La p es la property que está usando ese observable: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.properties/-delegates/observable.html