liveBook · Manning

Receiver type

As explained in Kotlin in Action, 2E

A receiver type is the class or interface type whose values can invoke an extension function or serve as the receiver for an extension function type.

A receiver type names the type that an extension attaches to. In `fun String.lastChar()`, `String` is the receiver type; in `"Kotlin".lastChar()`, the string value is the receiver object. This lets code add project-specific operations to existing JVM classes without rewriting or subclassing them, including final classes. section 3.3

package strings

fun String.lastChar(): Char = this.get(this.length - 1)

The type before the function name declares the receiver. Inside the body, `this` refers to that receiver and may usually be omitted, so its accessible members can be called directly. section 3.3

An extension looks like a member call, but it does not become a real member of the receiver class. It can use the receiver’s accessible methods and properties, but cannot access private or protected members. Extensions are syntactic sugar over static method calls, so they are resolved statically and cannot be overridden by subclasses. section 3.3 section 3.3.3

Choose a receiver type to constrain where an operation is available:

  • Use a broad type such as `Collection<T>` when an operation should work across lists, sets, and similar collections. section 3.3.3
  • Use a narrower type such as `Collection<String>` when only string collections should expose an operation; other element types produce a receiver-type mismatch. section 3.3.3
  • Use the same receiver syntax for extension properties. Such properties need custom accessors and have no backing field or independent state. section 3.3.5

A receiver type in a lambda

Receiver types also occur in extension function types. `StringBuilder.() -> Unit` describes a lambda whose `StringBuilder` argument becomes an implicit `this`. Calling `sb.builderAction()` invokes the lambda as an extension-style call, allowing `append(...)` without qualifying `sb`. This form supports builder-style APIs such as `buildString`, `apply`, and `with`. section 13.2.1 section 13.2.1

Listing 13.2 Redefining buildString to take a lambda with a receiver
fun buildString(
    builderAction: StringBuilder.() -> Unit   #1
): String {
    val sb = StringBuilder()
    sb.builderAction()                        #2
    return sb.toString()
}
 
fun main() {
    val s = buildString {
        this.append("Hello, ")                #3
        append("World!")                      #4
    }
    println(s)
    // Hello, World!
}

Compare an ordinary `(StringBuilder) -> Unit` parameter with `StringBuilder.() -> Unit`: the latter moves the argument into receiver position and makes it implicit in the lambda body. section 13.2.1

A receiver type improves calling syntax without changing the underlying class. For Java interoperability, extension properties are accessed through explicit generated getter and setter calls rather than Kotlin’s property syntax. section 3.3.5

How Kotlin in Action, 2E covers it

How deep it goes
Defines declaration and call syntax, encapsulation limits, static dispatch, type narrowing, properties, and receiver lambdas.
Examples
Uses `String.lastChar`, generic `Collection.joinToString`, string-only `join`, mutable `StringBuilder.lastChar`, and `buildString`.
What you can do after
Apply receiver types to define targeted extensions and receiver lambdas with member-like call syntax.
How it connects
Connects extension functions, extension properties, static dispatch, lambdas with receivers, apply, and with.

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.