Kotlin Scope Functions


Scope functions is one of the Kotlin feature I really like. When using such a function on an object, you are executing a block of code within the context of that object. You won’t find a similar feature in Java. There are five scope functions available in Kotlin: let, apply, run, with and also. In fact all of them is doing the same thing – execute a block of code on an object. However, there are some differences and we will discuss them on the simple example of code.

Example

The source code repository used for this article is available on my GitHub in repository sample-kotlin-playground. It is available here: https://github.com/piomin/sample-kotlin-playground.git.

Apply

Apply refer to the context object as a lambda receiver – by keyword this. It is the same as “you would be inside” the class of that object. You can even omit the keyword this. As a results it just returns the context object. All these features makes it ideal for changing value of object fields. Let’s take a look on the following example of code. Then we will try to implement the same functionality using other scope function.

@Test fun testApplyFunction() {
  val p: Person = Person("John", "Smith", 1)
    .apply {
      age = 20
      localization = "London"
    }
  Assert.assertEquals(20, p.age)
  Assert.assertEquals("London", p.localization)
}

Let

Let is able to access the context object as a lambda argument. If the argument name is not specified, the object is accessed by the implicit default name it. It is returning the lambda result. What means that if we want to do the same thing with person object as in the previous sample we need to return exactly that object.

@Test fun testLetFunction() {
  val p: Person = Person("John", "Smith", 1)
    .let {
      it.age = 20
      it.localization = "London"
      it
    }
  Assert.assertEquals(20, p.age)
  Assert.assertEquals("London", p.localization)
}

Run

Run the same as apply function refers to the context object as a lambda receiver. But unlike apply it returns the lambda result instead of the context object. The test is very similar to the testApplyFunction – the only difference is that we have to return object using this keyword.

@Test fun testRunFunction() {
  val p: Person = Person("John", "Smith", 1)
    .run {
      age = 20
      localization = "London"
      this
    }
  Assert.assertEquals(20, p.age)
  Assert.assertEquals("London", p.localization)
}

Also

We can use also function to do some additional work after previous changes. Let’s implement our test with apply in slightly different way. Function also returns the context object and takes the context object as a lambda argument.

@Test fun testAlsoFunction() {
  val p: Person = Person("John", "Smith", 1)
    .apply {
      age = 20
      localization = "London"
    }
    .also {
      Assert.assertEquals(20, it.age)
      Assert.assertEquals("London", it.localization)
    }
}

TakeIf

TakeIf is not a scope function. It is in addition provided by the Kotlin standard library together with takeUnless. While takeIf returns this object if it matches the predicate, takeUnless returns the object if it doesn’t match the predicate and null if it does. Let’s take a look on the simple test that illustrates usage of this function.

@Test fun testTakeIfFunction() {
  var p: Person? = Person("John", "Smith", 1).takeIf { it.id > 1 }
  Assert.assertNull(p)
  p = Person("John", "Smith", 1).takeIf { it.id == 1 }
  Assert.assertNotNull(p)
}

4 thoughts on “Kotlin Scope Functions

  1. Hi Piotr,

    Thank for such an informative lesson, I really liked your teaching style. Explaining all the concepts with real code-based examples. It is a good approach.

    Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.