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)