Spec-Zone.ru › Kotlin 2

Лямбда-выражения с получателем

В этой главе вы узнаете, как использовать получатели с другим типом функций — лямбда-выражениями — и как они помогают создавать предметно-ориентированный язык.

Лямбда-выражения с получателем

В начальном туре вы узнали, как использовать лямбда-выражения. У лямбда-выражений также может быть получатель. В этом случае лямбда-выражения могут обращаться к любым функциям-членам или свойствам получателя, не указывая его явно каждый раз. Без этих дополнительных ссылок код легче читать и поддерживать.

Лямбда-выражения с получателем также известны как функциональные литералы с получателем.

Синтаксис лямбда-выражения с получателем отличается при определении типа функции. Сначала укажите получатель, который хотите расширить. Затем поставьте . и завершите определение типа функции. Например:

MutableList<Int>.() -> Unit

У этого типа функции есть:

  • MutableList<Int> в качестве получателя.

  • Нет параметров функции в круглых скобках ().

  • Нет возвращаемого значения: Unit.

Рассмотрим пример, в котором на холсте рисуются фигуры:

class Canvas {
    fun drawCircle() = println("🟠 Drawing a circle")
    fun drawSquare() = println("🟥 Drawing a square")
}

// Lambda expression with receiver definition
fun render(block: Canvas.() -> Unit): Canvas {
    val canvas = Canvas()
    // Use the lambda expression with receiver
    canvas.block()
    return canvas
}

fun main() {
    render {
        drawCircle()
        // 🟠 Drawing a circle
        drawSquare()
        // 🟥 Drawing a square
    }
}

В этом примере:

  • Класс Canvas содержит две функции, имитирующие рисование круга или квадрата.

  • Функция render() принимает параметр block и возвращает экземпляр класса Canvas.

  • Параметр block — это лямбда-выражение с получателем, где получателем является класс Canvas.

  • Функция render() создает экземпляр класса Canvas и вызывает лямбда-выражение block() для экземпляра canvas, используя его в качестве получателя.

  • Функция main() вызывает функцию render() с лямбда-выражением, которое передается параметру block.

  • Внутри лямбда-выражения, переданного функции render(), программа вызывает функции drawCircle() и drawSquare() для экземпляра класса Canvas.

    Поскольку функции drawCircle() и drawSquare() вызываются в лямбда-выражении с получателем, их можно вызывать напрямую, как если бы они находились внутри класса Canvas.

Лямбда-выражения с получателем полезны, когда нужно создать предметно-ориентированный язык (DSL). Поскольку вы можете обращаться к функциям-членам и свойствам получателя, не ссылаясь на него явно, код становится лаконичнее.

Рассмотрим пример настройки пунктов меню. Начнем с класса MenuItem и класса Menu, содержащего функцию добавления пунктов в меню под названием item(), а также список всех пунктов меню items:

class MenuItem(val name: String)

class Menu(val name: String) {
    val items = mutableListOf<MenuItem>()

    fun item(name: String) {
        items.add(MenuItem(name))
    }
}

Для начала передадим лямбда-выражение с получателем в качестве параметра функции (init) функции menu(), которая создает меню:

fun menu(name: String, init: Menu.() -> Unit): Menu {
    // Creates an instance of the Menu class
    val menu = Menu(name)
    // Calls the lambda expression with receiver init() on the class instance
    menu.init()
    return menu
}

Теперь можно использовать DSL для настройки меню и создать функцию printMenu(), которая выводит структуру меню в консоль:

class MenuItem(val name: String)

class Menu(val name: String) {
    val items = mutableListOf<MenuItem>()

    fun item(name: String) {
        items.add(MenuItem(name))
    }
}

fun menu(name: String, init: Menu.() -> Unit): Menu {
    val menu = Menu(name)
    menu.init()
    return menu
}

//sampleStart
fun printMenu(menu: Menu) {
    println("Menu: ${menu.name}")
    menu.items.forEach { println("  Item: ${it.name}") }
}

// Use the DSL
fun main() {
    // Create the menu
    val mainMenu = menu("Main Menu") {
        // Add items to the menu
        item("Home")
        item("Settings")
        item("Exit")
    }

    // Print the menu
    printMenu(mainMenu)
    // Menu: Main Menu
    //   Item: Home
    //   Item: Settings
    //   Item: Exit
}
//sampleEnd

Как видите, использование лямбда-выражения с получателем значительно упрощает код, необходимый для создания меню. Лямбда-выражения полезны не только для настройки и создания, но и для конфигурирования. Их часто используют при создании DSL для API, UI-фреймворков и конструкторов конфигурации, чтобы сделать код лаконичнее и упростить работу с его структурой и логикой.

В экосистеме Kotlin есть множество примеров этого шаблона проектирования, например функции buildList() и buildString() из стандартной библиотеки.

Лямбда-выражения с получателями можно сочетать с типобезопасными конструкторами в Kotlin, чтобы создавать DSL, выявляющие проблемы с типами во время компиляции, а не во время выполнения. Подробнее см. в разделе Типобезопасные конструкторы.

Практика

Упражнение 1

У вас есть функция fetchData(), принимающая лямбда-выражение с получателем. Измените лямбда-выражение, чтобы использовать функцию append() и получить следующий вывод: Data received - Processed.

fun fetchData(callback: StringBuilder.() -> Unit) {
    val builder = StringBuilder("Data received")
    builder.callback()
}

fun main() {
    fetchData {
        // Write your code here
        // Data received - Processed
    }
}
fun fetchData(callback: StringBuilder.() -> Unit) { val builder = StringBuilder("Data received") builder.callback() } fun main() { fetchData { append(" - Processed") println(this.toString()) // Data received - Processed } }

Упражнение 2

У вас есть класс Button и классы данных ButtonEvent и Position. Напишите код, который вызывает функцию-член onEvent() класса Button, чтобы инициировать событие двойного щелчка. Код должен вывести "Double click!".

class Button {
    fun onEvent(action: ButtonEvent.() -> Unit) {
        // Simulate a double-click event (not a right-click)
        val event = ButtonEvent(isRightClick = false, amount = 2, position = Position(100, 200))
        event.action() // Trigger the event callback
    }
}

data class ButtonEvent(
    val isRightClick: Boolean,
    val amount: Int,
    val position: Position
)

data class Position(
    val x: Int,
    val y: Int
)

fun main() {
    val button = Button()

    button.onEvent {
        // Write your code here
        // Double click!
    }
}
class Button { fun onEvent(action: ButtonEvent.() -> Unit) { // Simulate a double-click event (not a right-click) val event = ButtonEvent(isRightClick = false, amount = 2, position = Position(100, 200)) event.action() // Trigger the event callback } } data class ButtonEvent( val isRightClick: Boolean, val amount: Int, val position: Position ) data class Position( val x: Int, val y: Int ) fun main() { val button = Button() button.onEvent { if (!isRightClick && amount == 2) { println("Double click!") // Double click! } } }

Упражнение 3

Напишите функцию, создающую копию списка целых чисел, в которой каждый элемент увеличен на 1. Используйте предоставленный каркас функции, расширяющий List<Int> функцией incremented.

fun List<Int>.incremented(): List<Int> {
    val originalList = this
    return buildList {
        // Write your code here
    }
}

fun main() {
    val originalList = listOf(1, 2, 3)
    val newList = originalList.incremented()
    println(newList)
    // [2, 3, 4]
}
fun List<Int>.incremented(): List<Int> { val originalList = this return buildList { for (n in originalList) add(n + 1) } } fun main() { val originalList = listOf(1, 2, 3) val newList = originalList.incremented() println(newList) // [2, 3, 4] }
  • Предыдущий шаг

  • Следующий шаг

01 июля 2026
Функции области видимостиКлассы и интерфейсы

© 2010–2026 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/kotlin-tour-intermediate-lambdas-receiver.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API