Kotlin Extensions
Kotlin Extensions
Kotlin extensions let you add new functions and properties to existing classes without modifying their source code or using inheritance. The compiler resolves extension calls statically, meaning they do not actually insert members into the target class. Instead, they compile to static functions with the receiver as the first parameter. Extensions are one of Kotlin's most distinctive features, and understanding both their power and their limitations is essential for writing maintainable code. By the end of this lesson, you will be able to:
- Explain how extension functions and properties are compiled.
- Describe how static dispatch affects extensions and member function conflicts.
- Identify practical use cases for extensions in Android development.
- Recognize the pitfalls of overusing extensions in large codebases.
Extension Functions
An extension function is declared by prefixing the function name with the receiver type. Inside the function body, this refers to the receiver object:
fun Int.isEven(): Boolean {
return this % 2 == 0
}
val result = 4.isEven() // true
The compiler transforms this into a static method where the receiver becomes the first parameter. The decompiled bytecode equivalent is a static function that takes an int receiver as its first argument and returns the result of receiver % 2 == 0. No modification to the Int class occurs at the bytecode level.
This means extensions cannot access private or protected members of the receiver class. They can only use the public API of the receiver type, which is an important distinction from member functions that have full access to the class internals.
Extension Properties
Extension properties follow the same pattern but cannot store state. They are limited to computed getters (and setters for mutable receivers) because there is no backing field:
val String.firstChar: Char
get() = this[0]
val letter = "Hello".firstChar // 'H'
This is syntactic sugar for a static function that takes the receiver and returns the computed value. Attempting to declare a backing field in an extension property produces a compile error.
Static Dispatch and Member Priority
Extensions are resolved statically based on the declared type of the variable, not the runtime type. If a class has a member function with the same signature as an extension function, the member always wins:
class Logger {
fun log(message: String) = println("Member: $message")
}
fun Logger.log(message: String) = println("Extension: $message")
Logger().log("test") // prints "Member: test"
This interview continues for subscribers
Subscribe to Dove Letter for full access to exclusive interviews about Android and Kotlin development.
Become a Sponsor