liveBook · Manning

Extension

As explained in Kotlin in Action, 2E

An extension function is a Kotlin function defined outside a class that can be called with member-like syntax on values of an existing receiver type.

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

How Kotlin in Action, 2E covers it

How deep it goes
Explains declaration, receivers, Java compilation, static dispatch, nullable receivers, and utility design; defers subclass syntax details.
Examples
Demonstrates String.lastChar, Collection.joinToString, User validation, nullable String checks, View/Button dispatch, and site-visit averages.
What it assumes
Builds from familiar Java-style classes and member calls, then introduces receiver types and receiver objects directly.
What you can do after
Apply it to add focused utilities and API adapters while avoiding assumptions about overriding or private-member access.
How it connects
Connects extension functions to Java interoperability, collections, nullable types, local functions, operator overloading, and higher-order functions.

Also explained in

Compare how 5 books explain extension →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Kotlin in Action, 2E. The section links point to the parts of the book it draws on.