Kotlin version 1.2.50
I have been following some examples on this tutorial on youtube https://www.youtube.com/watch?v=gPH9XnvpoXE. And there are a few things that I have understood, but there is still some confusion. I have left the comments in the code below where I am not sure what is happening.
fun main(args: Array<String>) {
val javaClient = createClient {
firstName = "joe"
lastName = "bloggs"
twitter {
handle = "@joebloggs"
}
}
println(javaClient.toConsole)
}
/* Are we passing in a lambda and receiver. What will the receiver be */
private fun JavaClientBuilder.twitter(suppler: JavaTwitterBuilder.() -> Unit) {
/* We call JavaTwitterBuilder().apply(..) Will apply return the newly created object? Not sure why we have to pass the suppler in the apply */
twitter = JavaTwitterBuilder().apply(suppler).build()
}
/* Are we passing in a lambda and receiver that return nothing */
private fun createClient(suppler: JavaClientBuilder.() -> Unit): JavaClient {
val javaClientBuilder = JavaClientBuilder()
/* confusion: Not sure about this, as we are calling suppler. Just wondering is the suppler the the JavaClientBuilder that was called in the above javaClient {} lambda */
javaClientBuilder.suppler()
return javaClientBuilder.build()
}
/* I understand this, an extension function that formats and returns the string from the JavaClient object it was called on */
private val JavaClient.toConsole: String
get() =
"Created client is: ${twitter.handle} ${company.name}"
/* Are we passing in a lambda and receiver. What will the receiver be */
private fun JavaClientBuilder.twitter(suppler: JavaTwitterBuilder.() -> Unit)
We've indeed got a receiver in this function, and it's the instance of JavaClientBuilder
that this function will be invoked on.
/* We call JavaTwitterBuilder().apply(..) Will apply return the newly created object? Not sure why we have to pass the suppler in the apply */
twitter = JavaTwitterBuilder().apply(suppler).build()
To understand how apply()
works, take a look at its source code (simplified version):
public inline fun <T> T.apply(block: T.() -> Unit): T {
block()
return this
}
It's an extension function declared on a receiver of type T
and returning an instance of T
, which takes in a block - another extension function with receiver of type T
returning Unit
. It's usually used to replace the Builder pattern - apply custom initialization logic to an object. In your case, suppler
is a block that contains initialization logic for an instance of JavaTwitterBuilder
. The function code creates the instance and uses apply()
with the logic in suppler
to initialize that instance.
/* Are we passing in a lambda and receiver that return nothing */
private fun createClient(suppler: JavaClientBuilder.() -> Unit): JavaClient
In this case, createClient()
doesn't have a receiver, it's a top-level function.
/* confusion: Not sure about this, as we are calling suppler. Just wondering is the suppler the the JavaClientBuilder that was called in the above javaClient {} lambda */
javaClientBuilder.suppler()
suppler
is a lambda with JavaClientBuilder
being the receiver type, which allows us to call it on the newly-created instance of JavaClientBuilder
.
/* I understand this, an extension function that formats and returns the string from the JavaClient object it was called on */
private val JavaClient.toConsole: String get() = "Created client is: ${twitter.handle} ${company.name}"
Right! Just a small correction, it's an extension property. Properties can have custom getters and setters. This property defines a custom getter, so indeed whenever this property is accessed it will produce a String with the format described by the getter code.
Hope the following example helps understand lambda with receiver type:
data class Person(val name: String)
fun getPrefixSafely(
prefixLength: Int,
person: Person?,
getPrefix: Person.(Int) -> String): String
{
if (person?.name?.length ?: 0 < prefixLength) return ""
return person?.getPrefix(prefixLength).orEmpty()
}
// Here is how getPrefixSafely can be called
getPrefixSafely(
prefixLength = 2,
person = Person("name"),
getPrefix = { x -> this.name.take(x) }
)
PS: These functional literals with receiver types are similar to extension functions IMO.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With