Extension functions add Kotlin-style operations to an existing type without editing its source or inheriting from it. Declare the receiver type before the function name, then call the function with ordinary member syntax. This is especially useful for adapting JDK, Android, and third-party APIs in mixed Kotlin and Java projects. section 3.0 section 3.3
Add a focused String operation
```kotlin fun String.lastChar(): Char = get(length - 1) fun main() { println("Kotlin".lastChar()) // n } ``` `String` is the receiver type and `"Kotlin"` is the receiver object. Inside the function, `this` refers to that receiver, so its accessible public members can be used directly. The extension looks like a member call, but it cannot access private or protected state. section 3.3
Use extensions to make utility behavior read naturally at the call site, while keeping classes focused on their essential API:
- Extend a broad receiver such as `Collection<T>` when the operation applies to lists, sets, and similar collections; default arguments keep calls concise. section 3.3.3
- Choose a narrow receiver such as `Collection<String>` when the operation is meaningful only for that element type; other receivers fail type checking. section 3.3.3
- Extract object-focused validation into an extension when it does not belong in the core class API and needs no private-data access. section 3.6
- Declare a nullable receiver, such as `String?`, when the operation itself should define null behavior; `this` must then be checked explicitly. section 7.10
Extensions are resolved statically, not virtually. A `View`-typed variable holding a `Button` selects the `View` extension, unlike an overridden member function. A real member with the same signature always wins, so extensions are not a mechanism for polymorphic customization. section 3.3.4
Static dispatch chooses the declared type
```kotlin fun View.showOff() = println("I'm a view!") fun Button.showOff() = println("I'm a button!") val view: View = Button() view.showOff() // I'm a view! ``` The receiver expression is declared as `View`, so the `View` extension is selected at compile time. Extensions cannot be overridden in subclasses. section 3.3.4
On the JVM, an extension function compiles to a static method whose first argument is the receiver. Kotlin member-like syntax adds no adapter objects or runtime overhead; Java calls the generated static method directly. Standard-library extensions similarly enrich Java collections and strings without converting or wrapping them. section 3.3.2 section 3.4.1
Listing 3.5 Declaring joinToString() as an extension
fun <T> Collection<T>.joinToString( #1
separator: String = ", ", #2
prefix: String = "", #2
postfix: String = "" #2
): String {
val result = StringBuilder(prefix)
for ((index, element) in this.withIndex()) { #3
if (index > 0) result.append(separator)
result.append(element)
}
result.append(postfix)
return result.toString()
}
fun main() {
val list = listOf(1, 2, 3)
println(
list.joinToString(
separator = "; ",
prefix = "(",
postfix = ")"
)
)
// (1; 2; 3)
}
Inspect a generic `Collection<T>.joinToString` extension with default parameters and a member-like call on a list. section 3.3.3




