25.3 Generics examples
After the theory, let’s move to some practical functions that will make our lives easier. Instead of
reinventing the wheel, I’m using three functions that are included in Kotlin standard library. These
functions let us do awesome things with just a generic implementation. They can inspire you to
create your own functions.
let
let
is a really simple function that can be called by any object. It receives a function that will receive
the object as a parameter, and returns the value that this function returns. It is really useful to deal
with nullable objects for instance. This is the definition:
25 Generics
124
1
inline fun T.let(f: (T) -> R): R = f(this)
It uses two generic types:
T
and
R
. The first one is defined by the calling object, and it’s the type that
the function receives. The second one is the result of the function.
How can we use it? You may remember that, when we were retrieving data from a data source, the
result could be null. We then returned a result mapped to the domain model if it wasn’t null, or just
a null reference otherwise:
1
if (forecast != null) dataMapper.convertDayToDomain(forecast) else null
This is really ugly, we shouldn’t need to deal with nullable types that way. And in fact we don’t
need to if we use
let
:
1
forecast?.let { dataMapper.convertDayToDomain(it) }
let
function will only be executed if
forecast is not null
thanks to ‘
?.
’ operator. It will return
null
otherwise. Just what we were trying to achieve.
with
We’ve talked a lot about this function during the book.
with
receives an object, and a function that
will be executed as an extension function. This means we can use
this
inside the function to refer
to the object. It will also return an object defined in the last line of the function.
1
inline fun with(receiver: T, f: T.() -> R): R = receiver.f()
Generics work the same way here:
T
stands for the receiver type, and
R
for the result. As you can
see, the function is defined as an extension function by using this declaration:
f: T.() -> R
. That’s
why we can then call
receiver.f()
.
We have several examples throughout the app:
1
fun convertFromDomain(forecast: ForecastList) = with(forecast) {
2
val daily = dailyForecast.map { convertDayFromDomain(id, it) }
3
CityForecast(id, city, country, daily)
4
}
apply
It may look very similar to
with
function, but the idea is a bit different.
apply
can be used to avoid
the creation of builders, because the object that calls the function can initialise itself the way it needs,
and the
apply
function will return the same object:
25 Generics
125
1
inline fun T.apply(f: T.() -> Unit): T { f(); return this }
We only need one generic type here, because the object that calls the function is also the one it’s
returned. A nice simple example would be:
1
val textView = TextView(context).apply {
2
text = "Hello"
3
hint = "Hint"
4
textColor = android.R.color.white
5
}
It creates a
TextView
, modifies some properties, and assigns it to a variable. Everything in a simple,
readable and compact syntax. But let’s use it in our current code. In
ToolbarManager
, we were doing
this to create the navigation drawable:
1
private fun createUpDrawable() = with(DrawerArrowDrawable(toolbar.ctx)) {
2
progress = 1f
3
this
4
}
Using
with
and returning
this
is clearly something that can be done easier by using
apply
:
1
private fun createUpDrawable() = DrawerArrowDrawable(toolbar.ctx).apply {
2
progress = 1f
3
}
You can review some more little improvements in Kotlin for Android Developers repository.
Do'stlaringiz bilan baham: |