apply()

Most programmers have run into cases where they had to repeat a reference to a variable or something a lot:

import java.util.Calendar

val sometime = Calendar.getInstance()

sometime.set(Calendar.YEAR, 1980)
sometime.set(Calendar.MONTH, 1)
sometime.set(Calendar.DAY_OF_MONTH, 22)
sometime.set(Calendar.HOUR_OF_DAY, 17)
sometime.set(Calendar.MINUTE, 0)
sometime.set(Calendar.SECOND, 0)
sometime.set(Calendar.MILLISECOND, 0)

println(sometime)

You have some object and you need to call a whole bunch of methods on it to configure it, such as setting the individual fields of a Calendar object, as we are doing here.

We could simplify this with let():

import java.util.Calendar

val sometime = Calendar.getInstance()

sometime.let {
  it.set(Calendar.YEAR, 1980)
  it.set(Calendar.MONTH, 1)
  it.set(Calendar.DAY_OF_MONTH, 22)
  it.set(Calendar.HOUR_OF_DAY, 17)
  it.set(Calendar.MINUTE, 0)
  it.set(Calendar.SECOND, 0)
  it.set(Calendar.MILLISECOND, 0)
}

println(sometime)

However, apply() works even better for this scenario. You call it on some object, supplying a lambda expression. The object becomes this inside of the lambda expression, allowing you to just call functions on it without having to name it.

Alas, Kotlin/JS does not have java.util.Calendar, so we cannot use that in the Klassbook. So, let’s look at an alternative construct:

data class IntPropertyBag(private val pieces: MutableMap<String, Int> = mutableMapOf()) {
  fun set(key: String, value: Int) {
    pieces[key] = value
  }
}

fun main() {
  val sometime = IntPropertyBag()

  sometime.apply {
    set("ID", 330258648)
    set("YEAR", 1979)
    set("HOW_MANY_ROADS_MUST_A_MAN_WALK_DOWN", 42)
  }

  println(sometime)
}

Here, pretend that IntPropertyBag is some truly awful class that you are getting from some third-party library. You have no choice but to use it, despite the fact that it’s API is fairly limited: you can give it one named integer at a time via set(). Here, rather than spell out the sometime. part of calling set() three times to set three integer properties, we use apply() to make this be the bag, so we can call set() without referencing the bag directly.

Also, apply() returns whatever object that you called it on, so we can further simplify this as:

data class IntPropertyBag(private val pieces: MutableMap<String, Int> = mutableMapOf()) {
  fun set(key: String, value: Int) {
    pieces[key] = value
  }
}

fun main() {
  val ultimateStuff = IntPropertyBag().apply {
    set("ID", 330258648)
    set("YEAR", 1979)
    set("HOW_MANY_ROADS_MUST_A_MAN_WALK_DOWN", 42)
  }

  println(ultimateStuff)
}

This is the same as the previous example, except that we are just chaining apply() right onto the call to the IntPropertyBag constructor.

Of course, we could also put the println() in here:

val ultimateStuff = IntPropertyBag().apply {
  set("ID", 330258648)
  set("YEAR", 1979)
  set("HOW_MANY_ROADS_MUST_A_MAN_WALK_DOWN", 42)

  println(this)
}

Or, we could eliminate ultimateStuff entirely:

println(IntPropertyBag().apply {
  set("ID", 330258648)
  set("YEAR", 1979)
  set("HOW_MANY_ROADS_MUST_A_MAN_WALK_DOWN", 42)
})

In summary, apply():


Prev Table of Contents Next

This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.