Interview preparation and exercises
Common interview traps
- A lambda is not automatically asynchronous. It runs wherever and whenever the caller invokes it.
inlinedoes not mean “run on another thread” or “always faster.”- An extension function does not modify or inherit from the receiver class.
reifiedrequiresinlineand does not defeat all JVM type erasure.UnitandNothingare different:Unitreturns normally;Nothingnever does.- A higher-order function can allocate function objects unless inlining or compiler optimization removes them.
- Captured Android objects can contribute to memory leaks when ownership is wrong.
Interview questions
What is a higher-order function?
A function that accepts another function, returns one, or both. map, filter, and callback-based APIs are common examples.
What does a lambda capture?
It keeps access to values declared outside its body. Captures are useful, but mutable state, threading, and Android lifecycle ownership must be considered.
Why use inline?
For small higher-order functions it can remove lambda objects and indirect calls. It also enables non-local returns and reified type parameters. Large or widely called inline functions can increase generated code size.
What are noinline and crossinline?
noinline keeps a lambda as a normal value so it can be stored or passed. crossinline still allows inlining but forbids a non-local return when the lambda is invoked from another context.
Why is reified useful?
It exposes the actual generic type inside an inline function, enabling checks such as value is T and access to T::class despite normal JVM type erasure.
Lambda versus anonymous function?
Both create function values. Anonymous functions use fun(...) syntax, can declare a return type directly, and an unlabelled return exits the anonymous function rather than an enclosing function.
Runnable interview exercise
inline fun <reified T> Any?.safeAs(): T? = this as? T
Before running it, predict all four results:
Open in Kotlin PlaygroundAndroid, null, null, and 42.Exercises
- Write
fun String?.orUnknown(): Stringand explain why it is an extension on a nullable receiver. - Create a lambda of type
(Double, Int) -> Doublethat calculates price times quantity. - Write a higher-order
retryfunction that accepts an attempt count and synchronous operation. Decide how failure should be represented. - Rewrite a nested collection chain as a loop, then compare readability and allocations.
- Create an inline function with one
noinlinecallback and explain why that callback must remain an object. - Expand
safeAsabove and explain why the safe cast returnsnullinstead of throwing. - Design a Compose screen API with state and event lambdas. Explain when several lambdas should become one event type.
Chapter checklist
- I can explain block-body and expression-body functions.
- I understand default arguments, named arguments,
Unit, andNothing. - I can read and write function types and lambdas with receivers.
- I know that lambdas are not automatically asynchronous.
- I can design and test a higher-order function.
- I understand captures and Android lifecycle risks.
- I can explain what inline copies and why code size may increase.
- I know the purpose of
noinlineandcrossinline. - I can explain type erasure and the limit of reified types.
Kotlin functions support defaults, named arguments, extensions, expression bodies, and function types. Lambdas are function values and may capture outside state. Higher-order functions accept or return functions, which powers collection operators and UI callbacks. inline can remove some lambda overhead and enables non-local returns; noinline preserves a lambda as an object, while crossinline forbids non-local returns. A reified type parameter uses inlining to make the actual generic type available at the call site, although nested generic types are still limited by JVM erasure.