In
For folks who got lost on the word "extension functions" - it's a way to attach a function or property to the instances of the existing classes. For example, val d = 10.twice()
It's very much like Util
twice(int)
but done in a very clean way. Visually it looks like you're calling a member of the class, but in reality, the compiler calls your function passing receiver as an argument.
T.let
let
allows passing given reciever into the parameter of the lambda, returns result from it. Essentially it's an map
operator on the single value
val imageData = db.getUserById(userId)
?.let { it.getProfilePicture().url }
?.let { fetchData(it) }
How to use?
It's very useful when it comes to the conditional on nullable object, think of functional replacement for if (user != null) user.ban()
:
user?.let { it.ban() }
// or even
user?.let(::ban)
Often we use expressions in the string interpolation, .let
can be very handy:
println("Hello ${db.getUser(id)?.let { it.name } ?: "NONAME"}")
T.apply
apply
calls given lambda in the context of receiver, similar to the with
found in JavaScript and Groovy.
User().apply {
name = "user name"
password = "qwerty"
}
How to use?
I found it's a way to patch encapsulate configuration/finish object initialisation. It helps to perform number of operations within lambda block which are logically related to the operation such as factory initialisation or object creation. apply
returns the receiver object(object it was called on)
So instead of
val awsS3Client = AwsS3Client()
val credentials = AwsCredentialsFactory("key", "secret")
credentials.setForceHttps(true)
awsS3Client.credentials = credentials
awsS3Client.bucket = "my_bucket"
We can have nicely organized initialization block:
val awsS3Client = AwsS3Client().apply {
credentials = AwsCredentialsFactory("key", "secret").apply {
setForceHttps(true)
}
bucket = "my_bucket"
}
Nice and clean, without messy noise and scope pollution!
T.also
It behaves exactly as .let
with one exception - with return result. .also
takes receiver and passes it as an argument for the given lambda, returning the same receiver
val bannedUser = db.getUserById(userId)
.also { println("Got user $it") }
.ban()
How to use?
From my experience it can be used for debugging - as demonstrated above. Also, it can be used a replacement for .apply
.apply
:
val awsS3Client = AwsS3Client().also {
it.credentials = AwsCredentialsFactory("key", "secret").apply {
it.setForceHttps(true)
}
it.bucket = "my_bucket"
}
T.run
/ with()
These have very similar apply
the result of the function would be something lambda returned
val userId = User().run {
name = "username"
password = "foobar"
db.save(this).id
}
val userId = with(User()) {
name = "username"
password = "foobar"
db.save(this).id
}
These two have identical behaviour - in both cases object is initialised, saved and generated id is returned - the result of the last line expression
How to use?
Well, I didn't find it used often in my code, but the example above may give you some idea
Combo
Some examples from the real projects
// filter out images with faces, print debug info, annotate
// image with labels and filter images by stop words
val images = allImages
// filter pics with faces if necessary
?.let { if (allowFaces) it else withoutFaces(word, it) }
?.also { log.info("findImageForWord: got ${it.size} pics after face filtering") }
// image labelling
?.let { findLabels(word, it) }
// exclude stop list words
?.filterNot { it.normalizedLabels.any { it in stopList } }
// Prepare video upload request for the YouTube API:
val video = Video().also {
it.snippet = VideoSnippet().also {
it.set("categoryId", "27")
it.set("description", description)
it.set("title", title)
it.set("tags", tags)
}
it.status = status
}
I hope you found this article useful for you. Check out other posts about functional constructions to get the most out of kotlin