By definition, Scoped functions are functions that execute a block of code within the context of an object.
Kotlin “scope functions” are functions that allow for changing the scope, or the range, of a variable. There are five such functions included in the Kotlin standard library: apply, run, with, let, and also.
before getting started into scope functions, I have explained the basics of kotlin in detail.
Beginning Android Development with Kotlin
Kotlin High Order Functions and Lambdas Explained
Kotlin Coroutines for Android Development
koin – Kotlin Dependency Injection In Android
Ktor – Networking Client For Android
MVVM with Kotlin Coroutines and Retrofit [Example]
Object References
To use these functions, we must understand a couple of things.
Inside a lambda of a scope function, this (lambda receiver) is used to access the context object. The same object can be accessed by using it (lambda argument). In general, both can perform similar things.
This
Inside a scope function, you will be able to reference the context object by a short word (this), instead of the name itself.
run, apply, with use ‘this’ to access the context object.
Note: We can exclude this keyword to refer to members of the class.
fun main() {
var user = User("Joe","London")
val username = with(user) {
name = "Palani"
address = "India"
println("Name = $name , Address = $address")
name
}
}
data class User(var name: String, var address: String)
It
‘let’ and ‘also’ functions refer to the object’s context as a lambda argument.
fun main() {
var user = User("Joe","London")
user.let {
println("Name : ${it.name}")
println("Address : ${it.address}")
}
}
data class User(var name: String, var address: String)
Return values
There are two types of return values that a scope function can return:
Lambda result
If we write any expression at the end of the code block, it becomes the return value for the scope function. Return value for ‘let’, ‘run’, and ‘with’ functions is the lambda result.
fun main() {
var user = User("Joe","London")
val updatedUsername = user.let {
it.name = "John"
it.name
}
println("Updated Username : $updatedUsername")
}
data class User(var name: String, var address: String)
Context object
‘apply’ and ‘also’ functions return the context object itself. In this case, we don’t need to specify the return value. The context object is automatically returned.
fun main() {
var user = User("Joe","London")
val updatedUser = user.apply {
name = "John"
}
println("Updated Username : ${updatedUser.name}")
}
data class User(var name: String, var address: String)
Check the below table for the comparison.
It | This | |
---|---|---|
Return Result | let | run, with |
Return object | also | apply |
let’s see the kotlin scope functions in detail.
Let
Mostly used for null checks, when applying ?.let on an object, we can rest safe that every time we access that object inside the scope function, the object will be not null. To reference the object inside the scope function, we use the keyword it.
- *Context Object – it*
- *Returns – last statement*
- Use case – let function is often used to provide null safety calls. Use safe call operator(?.) with ‘let’ for null safety. It executes the block only with the non-null value.
fun main() {
var user:User? = null
user?.let {
println("First Time : User $user")
}
user = User("John","India")
user?.let {
println("Second Time : User $user")
}
}
data class User(var name: String, var address: String)
Run
The ‘run’ function can be said as the combination of ‘let’ and ‘with’ functions.
- Context Object – this
- Returns – last statement
- Use Case – Used when the object lambda contains both initialization and the computation of the return value. Using run we can perform null safety calls as well as other computations.
It is the only scope function that has two variants.
run as an extension
used to create a scope to run an operation over an object, and get a result.
fun main() {
val user = User("mani","India")
val username = user?.run {
name = generateFullName(name)
name
}
println(username)
}
fun generateFullName(name: String) : String {
return name + " kandam"
}
data class User(var name: String, var address: String)
Note: run returns the last statement of the function.
run as function
reduce the scope of certain variables, to avoid unnecessary access.
fun main() {
val user = User("mani","India")
val username = run {
generateFullName(user.name)
}
println(username)
}
fun generateFullName(name: String) : String {
return name + " kandam"
}
data class User(var name: String, var address: String)
With
Similar to apply function, with is also used to change properties of an instance i.e. object configuration. The only difference is with is not an extension function. The last expression of with function returns a result.
- Context Object – this
- Returns – last statement
- Use Case – Run multiple operations on an object
fun main() {
val user = User("mani","India")
val username = with(user) {
name = "Sathish"
address = "USA"
name
}
println(username)
}
data class User(var name: String, var address: String)
Apply
Basically, if you are initializing an object, and setting a bunch of properties like in this case, you have a pretty solid candidate to apply this scope function.
- Context Object – this
- Returns – same object
- Use Case – Initialize and configure an object
fun main() {
val user = User("mani","India")
val updatedUser = user.apply {
name = "Siva"
address = "London"
}
println(updatedUser)
}
data class User(var name: String, var address: String)
Also
A common use also is for side effects without modifying the object. We can use it for doing some operations on the intermediate results. also does not transform the object. It returns the same object.
- Context Object – it
- Returns – same object
- Use Case – It is used where we have to perform additional operations when we have initialized the object members.
fun main() {
val user = User("mani","India")
val updatedUser = user.apply {
name = "Siva"
address = "London"
}.also {
println(it)
}
}
data class User(var name: String, var address: String)
That’s it. Thanks for reading.
Leave a Reply