Extension functions in depth
Extensions are resolved statically using the declared receiver type.
open class Animal
class Dog : Animal()
fun Animal.label() = "Animal"
fun Dog.label() = "Dog"
val animal: Animal = Dog()
println(animal.label()) // Animal
The runtime object is Dog, but the variable is declared as Animal; extension dispatch is not virtual.
Nullable receiver
fun String?.orUnknown(): String = this?.takeIf { it.isNotBlank() } ?: "Unknown"
The extension handles null itself, so it can be called on a nullable variable.
Member wins over extension
If a class member and visible extension have the same applicable signature, the member is called.
Scope extensions carefully
Extensions are best when they express natural behavior of the receiver and have visible dependencies.
✕ Hidden side effect
fun User.uploadAndTrackAndSave() { /* many hidden dependencies */ }
✓ Focused transformation
fun User.toAnalyticsProperties(): Map<String, String> = mapOf("id" to id.toString())
Interview-ready answer
Extensions improve call-site syntax but do not modify a class, access its private state, or provide virtual overriding. They are selected statically, and real members take priority.