Understand Kotlin Function Literal with Receiver by Example
This article provides some simple code examples of using function literal with receiver (also known as lambda/anonymous function with receiver).
I came across this lambda syntax - NavGraphBuilder.() -> Unit
and it turns out it is called Function Literal with Receiver, which is also known as Lambda/Anonymous Function with Receiver.
The syntax looks like this:
Receiver.(Parameters) → ReturnType
The following shows some examples of building a custom string using function literal with receiver.
Example 1: Function Literal With Receiver
fun buildCustomStringExample1(
action: StringBuilder.(String) -> Unit): String {
val stringBuilder = StringBuilder()
stringBuilder.action("Example1")
return stringBuilder.toString()
}
action
is the function literal / lambda function with receiver. StringBuilder
is the receiver. It acts like an extension function of StringBuilder
which takes in the string
as input parameter.
To call action
, StringBuilder
is instantiated, call it like an extension function - stringBuilder.action("Example1")
You can imagine
action
is like a callback function that belongs toStringBuilder
.
Usage
This is the usage of function literal with receiver:
val output1 = buildCustomStringExample1 { content ->
this.append("<tag>")
append("$content")
append("</tag>")
}
println("$output1")
We call the buildCustomStringExample1
with function literal / lambda function parameter. In this lambda function, we specify how we build the custom string - wrap the content with "<tag>"
and "</tag>"
.
content
is the input parameter which is passed in from the buildCustomStringExample1
function. this
is the StringBuilder
instance that created in buildCustomStringExample1()
function, and it can be omitted. append()
is the function that belongs to StringBuilder
.
[Updated - Jun 12, 22]: I recently learned that this type of usage is called domain-specify language (DSL). So, function literal with receiver is used to build DSL. The main purpose is for readability.
Output
The output looks like this:
<tag>Example1</tag>
Example 2: Function Literal Without Receiver
Function literal / lambda function with receiver can be rewritten without using the receiver based on the following syntax:
(Receiver, Parameters) → ReturnType
This is the usual lambda expressions which takes in 2 parameters. The first parameter is, StringBuilder
which is the receiver in example 1 above.
fun buildCustomStringExample2(
action: (StringBuilder, String) -> Unit
): String {
val stringBuilder = StringBuilder()
action(stringBuilder, "Example2")
return stringBuilder.toString()
}
action
is the usual callback function which takes in 2 parameters. To call action
, StringBuilder
is instantiated, and passed in as the first parameter of the action
callback function - action(stringBuilder, "Example2")
Usage
This is the usage of function literal without receiver:
val output2 = buildCustomStringExample2 { stringBuilder, content ->
stringBuilder.append("<tag>")
stringBuilder.append("$content")
stringBuilder.append("</tag>")
}
println("$output2")
This is similar to example 1 except we no longer use this
which has been replaced by stringBuilder
as the first lambda parameter. content
is the second parameter of the lambda function.
Output
The output looks like this, which has the same output as example 1:
<tag>Example2</tag>
Conclusion
It is not hard to understand function literal with receiver. It is basically a simplified way to write normal function literal / lambda function without additional parameter.
Function Literal with Receiver can be rewritten as Function Literal without Receiver.
Receiver.(Parameters) → ReturnType
---> (Receiver, Parameters) → ReturnType