Scope Functions Quiz
KOTLIN › Language Idioms
Which scope function returns the context object and exposes it as this?
- let returns the last expression and uses it as the context.
- also returns the receiver, but exposes it as it instead.
- apply returns the receiver and exposes it as this.
- with returns the last block result and uses this inside.
Answer: apply returns the receiver and exposes it as this.
apply returns the receiver itself and references it as this, making it ideal for configuration that returns the configured object.
You want to log a value as a side effect inside a call chain without changing what is passed along. Which is idiomatic?
- let, which maps the receiver to a new result via it
- also, which runs on it and returns the same object
- run, executing a block and returning that block's result
- with, a non-extension call returning the lambda's result
Answer: also, which runs on it and returns the same object
also returns the same object and exposes it as it, so it performs a side effect (logging) while the chain continues with the unchanged object.
What does this expression return: val n = str?.let { it.length }, where str is String??
- The original str value, since let just forwards it unchanged
- Always null, because safe calls skip the lambda every time
- An Int?: the lambda result, or null if str is null
- A String, because let converts the value back into text
Answer: An Int?: the lambda result, or null if str is null
let returns the lambda result; with ?. the lambda runs only when str is non-null, so n is the length or null otherwise.
Which pair both return the lambda result?
- run and with
- apply and also
- apply and let
- also and run
Answer: run and with
run and with both return the lambda result. apply and also are the two that return the context object instead.
Inside an apply block on a Person, how do you set its age property?
- it.age = 32
- age = 32
- this.it.age = 32
- value.age = 32
Answer: age = 32
apply exposes the object as this, so members can be accessed bare; age = 32 is equivalent to this.age = 32.
Which scope function is a non-extension that takes the object as an argument and exposes it as this?
- let
- also
- apply
- with
Answer: with
with is called as with(obj) { }, takes the object as an argument, uses this as the reference, and returns the lambda result.
Why might you prefer also over apply even when both return the object?
- also exposes the object as it, avoiding shadowing outer this
- also returns the lambda's result whereas apply returns the object
- also may only be applied to nullable receiver types like T?
- apply cannot be chained in a call sequence but also can
Answer: also exposes the object as it, avoiding shadowing outer this
Both return the object, but also uses it (a named/explicit reference) which reads better for side effects that pass the object as an argument and avoids shadowing an outer this.
What distinguishes the non-extension run { } from the extension form obj.run { }?
- It returns the context object rather than the lambda’s result
- It exposes the object as it instead of this in the block
- It has no receiver object and returns the block's last expression
- It can only be used at the top level of a file, not in expressions
Answer: It has no receiver object and returns the block's last expression
Non-extension run takes no object; it runs a block of statements and returns the last expression, which is handy for scoping temporary variables where an expression is required.
You have val config: Config? and want to call several of its methods only when it is non-null, then use the block's computed result. Which is most idiomatic?
- with(config) { ... }
- config?.run { ... }
- config.apply { ... }
- with(config!!) { ... }
Answer: config?.run { ... }
config?.run { } executes only when config is non-null, exposes it as this so methods are called bare, and returns the lambda result; with has no safe-call form and apply would return the object instead of a result.
Apart from null-checks, what is an idiomatic use of let?
- Configuring an object's properties and then returning that same object
- Doing a side effect and then passing the unchanged object down the chain
- Grouping calls on a non-null object that was passed in as an argument
- Mapping the receiver to a new result while keeping it in a small scope
Answer: Mapping the receiver to a new result while keeping it in a small scope
let returns the lambda result, so it suits mapping a value into a different type, and it limits the introduced variable (it) to the block's scope.
What does user.takeIf { it.isActive } evaluate to when isActive is false?
- null
- the user object
- false
- an exception is thrown
Answer: null
takeIf returns the receiver when the predicate is true and null otherwise, so a false predicate yields null (takeUnless is its inverse).
Kotlin's standard scope functions (let, run, with, apply, also) are declared inline. What does that imply?
- Lambda bodies inline at the call site, enabling non-local returns
- They may only be invoked from within other inline functions
- Their lambda bodies are always dispatched onto a background thread
- They cannot capture variables or vals from the enclosing scope
Answer: Lambda bodies inline at the call site, enabling non-local returns
Being inline, the compiler copies the lambda body into the call site, avoiding a runtime lambda allocation and permitting non-local returns from the enclosing function.
What is the value of x in: val x = 5.also { println(it + 1) }?
- 6
- 5
- Unit
- null
Answer: 5
also ignores the lambda's result and returns the context object itself, so x is 5 even though the lambda prints 6.
Inside a nested let, you need to reference the outer block's object as well as the inner one. What is the idiomatic fix?
- Use this@let to reach the shadowed outer receiver object
- Switch the inner block to apply, which exposes it as this
- Name lambda params, e.g. x.let { o -> y.let { i -> ... } }
- It is impossible, because nested let blocks are not allowed
Answer: Name lambda params, e.g. x.let { o -> y.let { i -> ... } }
let exposes the object as the implicit it, which the inner block shadows; naming the lambda parameters makes both objects accessible, and this@let is invalid because let uses it rather than a labeled receiver.