Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

A receiver parameter is an implicit parameter passed to a Kotlin extension function, extension property, or lambda that defines the execution context (this) for that block of code. By specifying a receiver type, the compiler exposes the public members of that type directly within the body without requiring explicit qualification. The receiver parameter is defined by prefixing the function name, property name, or function type with the target type, followed by a dot (.).

Extension Functions

When declaring an extension function, the type being extended is the receiver type. The instance on which the function is invoked becomes the receiver parameter.
fun String.printLength() {
    // 'this' represents the receiver parameter (the String instance)
    println(this.length) 
    
    // 'this' can be omitted when there is no shadowing
    println(length) 
}

// Invocation: "Kotlin" is passed as the receiver parameter
"Kotlin".printLength()

Extension Properties

Receiver parameters also apply to extension properties. Because extension properties cannot store state (they lack a backing field), the receiver parameter is utilized within custom getters and setters to compute or mutate the property value.
val String.isLong: Boolean
    get() = this.length > 10 // 'this' is the receiver parameter

// Invocation
val check = "Kotlin".isLong

Nullable Receivers

A receiver parameter can be declared as nullable by appending ? to the receiver type. This allows the extension to be invoked on null references without requiring the safe-call operator (?.). The function body is responsible for handling the nullability of the receiver parameter.
fun String?.printOrFallback() {
    // 'this' is the nullable receiver parameter
    if (this == null) {
        println("Fallback")
    } else {
        println(this)
    }
}

val text: String? = null
// Valid invocation without '?.' because the receiver parameter accepts null
text.printOrFallback() 

Function Literals with Receiver

In higher-order functions, defining a function type with a receiver (e.g., String.() -> String) transforms a standard lambda into an extension lambda, altering its lexical scope to include the receiver’s members. Crucially, at the type-system level, function types with receivers are interchangeable with standard function types that take the receiver as their first explicit parameter (e.g., (String) -> String).
// The type String.() -> String denotes a function with a String receiver
val transform: String.() -> String = {
    // 'this' is the implicit String receiver parameter
    this.uppercase() 
}

// Invocation via dot-syntax (passing the receiver parameter implicitly)
val result1 = "hello".transform()

// Invocation as a regular function (passing the receiver parameter explicitly)
val result2 = transform("hello")

JVM Compilation Mechanics

Kotlin does not modify the original classes to inject receiver parameters. At the JVM bytecode level, the Kotlin compiler translates the receiver parameter into an explicit first argument of the compiled method. For top-level extensions, the compiler generates a static method. For member extensions (extensions declared inside another class), the compiler generates an instance method where the extension receiver is passed as the first regular parameter. A top-level Kotlin extension function:
fun Int.add(value: Int): Int {
    return this + value
}
Is compiled to the equivalent Java representation:
public static final int add(int $this$add, int value) {
    return $this$add + value;
}

Static Resolution

Receiver parameters are resolved statically at compile time based on the declared type of the variable, not dynamically based on the runtime type. They do not participate in virtual method dispatch.
open class Shape
class Circle : Shape()

fun Shape.getName() = "Shape"
fun Circle.getName() = "Circle"

fun printName(shape: Shape) {
    // The receiver parameter is statically resolved to Shape
    println(shape.getName()) 
}

// Outputs "Shape", not "Circle"
printName(Circle())

Dispatch Receiver vs. Extension Receiver

When an extension function is declared as a member inside another class, two distinct implicit receivers exist in the same scope. It is critical to distinguish between them, as only one is a true receiver parameter:
  1. Dispatch Receiver: The instance of the enclosing class where the extension is declared. This is an implicit receiver that participates in virtual dispatch, but it is not a receiver parameter.
  2. Extension Receiver: The instance of the type being extended. This is the actual receiver parameter, and it is resolved statically.
When naming conflicts occur between the two receivers, the extension receiver (receiver parameter) takes precedence. To access the dispatch receiver, qualified this syntax is required.
class Connection(val host: String) {
    fun connect() { println("Connecting...") }
}

class Session(val host: String) { // Dispatch Receiver
    
    // Connection is the Extension Receiver (Receiver Parameter)
    fun Connection.execute() {
        // Resolves to Connection.host (Extension Receiver)
        println(host) 
        
        // Resolves to Session.host (Dispatch Receiver) via qualified 'this'
        println(this@Session.host) 
        
        // Resolves to Connection.connect()
        connect() 
    }
}
Master Kotlin with Deep Grasping Methodology!Learn More