As we already covered, Kotlin provides the ability to extend a class or an interface with new functionality without having to inherit from the class or use design patterns such as Decorator ⮕ Kotlin is fun - extension functions.
Kotlin also treats functions as first-class citizens, meaning they can be passed around. It also, of course, supports generics ⮕ Kotlin is fun - Function types, lambdas, and higher-order functions.
This means we can write something like this:
fun <T> T.doWith(op: (T) -> Unit) {
op(this)
}
This extension function can be called on anything and will simply pass this anything as an argument to the lambda passed as a parameter. For example:
val someList = listOf(1, 2, 3, 4)
someList.doWith ({ list ->
println(list.size)
println(list.sum())
})
This is syntax is not following the conventions. Let's rewrite it in proper Kotlin, by removing parenthesis and using the implicit it parameter:
someList.doWith { // it -> List<Int>
println(it.size)
println(it.sum())
}
This is better. Notice though that the it is redundant. We would rather be able to write:
someList.doWith { // this -> List<Int>
println(size)
println(sum())
}
But how? Read on!
Lambdas with receivers
To make this magic happen, the only necessity is to change the type of the op argument from (T) -> Unit to T.() -> Unit. This special syntax, A.(B), denotes a receiver object:
Inside the body of the function literal, the receiver object passed to a call becomes an implicit this, so that you can access the members of that receiver object without any additional qualifiers, or access the receiver object using a
thisexpression.
The final implementation is now:
fun <T> T.doWith(op: T.() -> Unit) {
op(this)
}
listOf(1, 2, 3, 4).doWith {
println(size)
println(this.sum()) // "this" is optional
}
It is just a bit of sugar-coating, but admit this syntax looks cool 😎.
Side note
In the real world, we would also want this doWith to be inline, to improve performances:
inline fun <T> T.doWith(op: T.() -> Unit) {
op(this)
}
For more information on receivers (and more examples), see the docs on Function literals with receivers.
Top comments (0)