Extension Functions and Scope Functions

This section documents Kotlin 2.4.x on the JVM, as published at kotlinlang.org, which is the reference these pages are written and verified against.

This content was generated with the assistance of AI and should be verified against kotlinlang.org before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

Extension functions let you add a method to a type you do not own; scope functions — built entirely as extension functions themselves — give a short, consistent vocabulary for running a block of code against one value.

Extension Functions and Properties

An extension function is declared outside the class it appears to extend, with the receiver type prefixed before the function name (ReceiverType.functionName). It compiles to an ordinary static-style function taking the receiver as a hidden first parameter — no subclassing, and no modification of the original type, actually happens:

fun String.isPalindrome(): Boolean {
    val cleaned = this.lowercase().filter { it.isLetter() }
    return cleaned == cleaned.reversed()
}

println("Racecar".isPalindrome())     // true -- called exactly like a real member function

val String.wordCount: Int              // extension property -- no backing field allowed (see Classes and Objects)
    get() = trim().split(Regex("\\s+")).size

println("the quick brown fox".wordCount)   // 4

This is how much of the Kotlin standard library extends java.lang.String/java.util.Collection and friends with idiomatic-feeling members (.trim(), .filter { }, .sum()) without touching the JDK classes themselves — see Strings and Text and Collections and Sequences for many such examples in practice. An extension is resolved statically, by the declared type at the call site, not dynamically by the runtime type — unlike a real member override.

The Five Scope Functions

let, run, with, apply, and also all execute a lambda "in the context of" a value; they differ only in (a) how the value is referenced inside the lambda (it vs. this) and (b) what the whole expression returns (the lambda’s result vs. the original value):

Function Receiver as Returns Typical use

let

it

lambda result

null-checking a nullable value, or transforming it into something else — x?.let { …​ }.

run

this

lambda result

initializing then computing a result from an object in one expression.

with

this

lambda result

calling several members on an existing (non-null) object without repeating its name — not an extension function itself, called as with(obj) { …​ }.

apply

this

the object itself

configuring an object’s properties right after construction, then returning it (chainable).

also

it

the object itself

a side effect (logging, an assertion) in the middle of a chain, without breaking the chain.

// let: transform a nullable value only if it isn't null
val length: Int? = maybeName?.let { it.trim().length }

// run: compute a result using "this"
val area = Rectangle(3, 4).run { width * height }

// with: several calls on one object, no "obj." prefix needed
val summary = with(StringBuilder()) {
    append("Name: ").append(name)
    append(", Age: ").append(age)
    toString()
}

// apply: configure, then return the same object -- classic builder-style usage
val request = HttpRequestBuilder().apply {
    url = "https://example.com"
    method = "GET"
}

// also: a side effect that doesn't change what's returned
val user = createUser("Ada").also { println("created user ${it.id}") }

Choosing Between Them

A practical rule of thumb: use apply/also when the point is the object itself (configuring it, or observing it, before continuing to use it); use let/run when the point is the result of a computation performed against it; reach for with specifically when calling several members on an object you already have in hand (not one you just created). All five are ordinary inline extension functions in the standard library — nothing about them is special-cased by the compiler beyond inline itself (see Lambdas and Higher-Order Functions).

See Also