Lesson 12 / 25
Extension Functions and Scope Functions
Add functions to types; let, apply, run, also, with.
Extending without inheritance
An extension function fun String.isEmail(): Boolean adds a function callable as "a@b.c".isEmail() without modifying or subclassing String; it is resolved statically and cannot access private members. Extension properties work similarly. The scope functions run a block with an object in context: let (object as it, returns the lambda result; common with ?.let for nullable values), also (as it, returns the object; side effects such as logging), apply (as this, returns the object; configuration), run (as this, returns the lambda result) and with(obj) { } (non-extension form of run).
Extensions and scope functions together
Pick the function by what it returns.
fun String.isEmail(): Boolean = contains("@") && substringAfter("@").contains(".")
val List<Int>.median: Double?
get() = if (isEmpty()) null else sorted().let { s ->
if (s.size % 2 == 1) s[s.size / 2].toDouble()
else (s[s.size / 2 - 1] + s[s.size / 2]) / 2.0
}
class Request { var url = ""; var timeoutMs = 0; val headers = mutableMapOf<String, String>() }
fun build(token: String?): Request = Request().apply { // configure, returns Request
url = "https://api.example.com"
timeoutMs = 5_000
token?.let { headers["Authorization"] = "Bearer $it" } // only if not null
}.also { println("built request for ${it.url}") } // side effect, returns RequestDo not nest scope functions deeply
Chains of let inside apply inside run quickly become unreadable because it and this change meaning. If a block grows, extract a named function.
Quick check: Which scope function returns the object itself and exposes it as `this`?
- `let`
- `apply`
- `run`
- `with`
Answer
`apply` — apply is for configuring an object; also returns it too but uses it.