with()

with() works nearly identically to run(). Both cause an object to become the this inside the lambda expression, and both return the results of the last statement in that lambda expression.

The difference lies in where the object comes from:

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

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

    toString()
  }

  println(ultimateStuff)
}

However, this introduces a subtle change: run() is more flexible with nullable types than is with(). With run(), you can use ?. to conditionally call run() if the object (the “receiver”) is not null:

  var thing: String? = "foo"

  val nonNullResult = thing?.run { this.isNotBlank() }

  println(nonNullResult)

  thing = null

  val nullResult = thing?.run { this.isNotBlank() }

  println(nullResult)

This prints:

true
null

In both cases, we are using thing?.run { this.isNotBlank() }, where isNotBlank() returns true if the string has one or more non-whitespace characters. In the case where thing is "foo", run() is called, because thing is not null. Where thing is null, though, the ?. skips the run() call, and so nullResult also winds up as null. We do not need ?. on the isNotBlank() call, because the Kotlin compiler knows that the ?.run() cannot wind up with a null value.

With with(), though, the safe-call operator (?.) is not an option — we pass the possibly-null value as the parameter. Our lambda expression would need to be able to deal with the possibly-null value.

In summary, with():


Prev Table of Contents Next

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