[Kotlin] lambda with receiver

Writing time : 2021-07-21 00:22:48

Specify the receiving object lambda

Receiving object-specific lambda means a lambda that can call a method of a specific object in a lambda expression.

Below is the code to pass the person object as a parameter to the with method and access the member of the person object in a lambda expression.
This is created before name and age.

data class Person(var name:String, var age: Int)  
val person = Person("John", 20)  

with, apply

If you look at the implementation part of with, you can access the object passed as the first argument by passing the receiving object-specified lambda as the second argument.

public inline fun <T, R> with(receiver: T, block: T.() -> R): R {  
    return receiver.block()  

In the same way as with, apply receives a lambda that specifies the receiving object as an argument.
The difference with with is that apply is defined as an extension function without receiving the receiving object as an argument, so that the receiving object-specified lambda can be directly passed to the apply method of the receiving object.

public inline fun <T> T.apply(block: T.() -> Unit): T {  
    return this  

Since apply executes the lambda and returns the receiving object, it is used to create an object as shown below, change the created object, and then return the changed object.
The sample below is a code that creates a Bundle object, adds data, and stores the created Bundle object in a variable.

val bundle = Bundle().apply {  
    putString("param1", "foo")  
    putString("param2", "bar")  

When apply is not used, the code is as follows.

val temp = Bundle()  
bundle.putString("param1", "foo")  
bundle.putString("param2", "bar")  
val bundle = temp;  

Handling Android Fragments

Let's look at another use case of receiving object-specified lambda.
The code below creates an extension function of FragmentManager and receives a lambda expression that specifies the receiving object as a parameter.

Execute the receiving object-specified lambda on the FragmentTransaction object created with the beginTransaction method and call the commit method of the FragmentTransaction object again.
Since the beginTransaction method and the commit method are fixed operations such as file open and close, they do not need to be changed.

Only the changed part is passed to the lambda expression so that various types of requests can be processed.
And if the processed content is limited to the processing of a specific object, the code implemented in the lambda expression can be written simply by using the receiving object-specified lambda.

private inline fun FragmentManager.transact(action: FragmentTransaction.() -> Unit) {  
    beginTransaction().apply {  

As the AppCompatActivity extension function, it receives the fragment and the id of the layout to include the fragment and calls the replace function.

fun AppCompatActivity.replaceFragmentInActivity(fragment: Fragment, frameId: Int) {  
    supportFragmentManager.transact {  
        replace(frameId, fragment)  

From now on, you can add a Fragment by calling only the replaceFragmentInActivity method in the class that inherited the AppCompatActivity class.

class ExsampleActivity: AppCompatActivity() {  
    override fun onCreate(savedInstanceState: Bundle?) {  
        replaceFragmentInActivity(ExampleFragment.newInstance(), R.id.fragment_container)  

For reference, the code below adds a Fragment without using a lambda.
If you add a Fragment in several activities, there is a disadvantage that the code below needs to be added repeatedly.

class ExsampleActivity: AppCompatActivity() {  
    override fun onCreate(savedInstanceState: Bundle?) {  
        val newFragment = ExampleFragment()  
        val transaction = supportFragmentManager.beginTransaction()  
        transaction.replace(R.id.fragment_container, newFragment)  
Previous post