Kotlin scope functions: let, run, with, apply, and also

  • Let
  • inline fun  T.let(block: (T) -> R): R {
      return block(this)
    }
    

    Let returns the result of lambda. Lambda takes as an input parameter the object upon let is invoked. Thus the object can be referred in the function scope as it. The function returns the last statement.

    val test = "my test string"
    val res = test
      .let { it.substring(0, 3) }
      .let { it.length}
    println(res) //3
    

    Let can be used for the null check:

    val test: String? = null
    test?.let {
      println("$it is not null") //prints nothing
    }
    
  • Also
  • inline fun T.also(block: (T) -> Unit): T {
      block(this)
      return this
    }
    

    Also returns the original object and the function invokes the lambda which takes as an input parameter the object which invoked also.

    data class Draft(var id: Long, var name: String)
    val draft = Draft(1L, "test")
    val res = draft.also {
      it.id = 2L
      it.name = "test2"
    }
    println(draft) //Draft(id=2, name=test2)
    println(res == draft) //true
    
  • Run
  • inline fun  T.run(block: T.() -> R): R {
      return block()
    }
    

    Run is an extension function of the object which is invoking run. So in the scope of the function the object can be referred by this (however this can be omitted). The function returns the last statement.

    data class Draft(var id: Long, var name: String)
    val draft = Draft(1L, "test")
    val res = draft.run {
      id = 2L
      name = "test2"
      name
    }
    println(draft) //Draft(id=2, name=test2)
    println(res) //test2
    

    Run can be used for the null check:

    val draft: Draft? = null
    draft?.run {
      println(draft) //prints nothing
    }
    
  • Apply
  • inline fun T.apply(block: T.() -> Unit): T {
      block()
      return this
    }
    

    Apply is an extension function of the object which is invoking apply. In the scope of the function the object can be referred by this. Apply returns the original object.

    data class Draft(var id: Long, var name: String)
    val draft = Draft(1L, "test")
    val res = draft.apply {
      id = 2L
      name = "test2"
    }
    println(draft) //Draft(id=2, name=test2)
    println(res == draft) //true
    
  • With
  • inline fun  with(receiver: T, block: T.() -> R): R {
      return receiver.block()
    }
    

    With is invoked with the object passed as an argument then an extension function is called on it. In the scope of the function the object can be referred by this. The last expression of with function returns a result.

    data class Draft(var id: Long, var name: String)
    val draft = Draft(1L, "test")
    val res = with(draft) {
      id = 2L
      name = "test2"
      id
    }
    println(draft) //Draft(id=2, name=test2)
    println(res) //2
    

Please find the link to the Kotlin documentation regarding the conventions of using the scope functions.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s