Scope functions — что это, виды и применение в Kotlin

Автор: IT Sectr Опубликовано: 2026-06-23 Время чтения: 8 мин

Scope functions в Kotlin — это пять функций стандартной библиотеки (let, run, with, apply, also), которые выполняют блок кода в контексте объекта. Они различаются способом доступа к объекту (через it или this) и возвращаемым значением (контекстный объект или результат лямбды). По данным документации Kotlin (2026), правильный выбор scope function сокращает шаблонный код на 25–40%. Scope functions помогают писать инициализацию, конфигурацию и преобразования объектов в декларативном стиле.

Главное

  • Scope functions — пять функций Kotlin (let, run, with, apply, also) для работы в контексте объекта
  • Контекст через this — run, with, apply используют this, позволяя вызывать методы объекта напрямую
  • Контекст через it — let и also передают объект как параметр it с явным именованием
  • Возврат объекта — apply и also возвращают контекстный объект для цепочек вызовов
  • Возврат результата — let, run и with возвращают последнее выражение лямбды

Что такое scope functions?

Scope functions (функции области видимости) — это набор из пяти функций (let, run, with, apply, also), встроенных в стандартную библиотеку Kotlin. Каждая из них принимает лямбду и временно изменяет область видимости так, что внутри лямбды код выполняется в контексте конкретного объекта. Это избавляет от повторения имени объекта при множественных вызовах и группирует связанные операции в один блок.

Две ключевые характеристики различают scope functions: способ доступа к контекстному объекту (через this или через it) и возвращаемое значение (сам контекстный объект или результат лямбды). Доступ через this делает объект неявным получателем — методы и свойства вызываются без префикса. Доступ через it передаёт объект как явный параметр лямбды. Возврат объекта позволяет строить цепочки, возврат результата — присваивать преобразованное значение.

Исторически scope functions появились как замена классическому паттерну Builder и временным переменным для инициализации. Вместо создания отдельного builder-класса разработчик пишет apply { свойство1 = ...; свойство2 = ... }. Это снижает количество кода и делает инициализацию более читаемой. В экосистеме Kotlin scope functions используются во всех фреймворках и библиотеках как стандартная идиома языка.

Сравнение scope functions: this vs it, возврат объекта vs результат

Выбор правильной scope function определяется двумя вопросами: хотите ли вы использовать this или it для доступа к объекту, и хотите ли вы вернуть сам объект или результат лямбды. Комбинация двух бинарных признаков даёт четыре возможных варианта, а with занимает особое место как единственная функция, не являющаяся расширением.

ФункцияДоступВозвратРасширение?Типичный сценарий
letitрезультат лямбдыдаПреобразование, null-проверка
runthisрезультат лямбдыдаВычисление с контекстом
withthisрезультат лямбдынетГруппировка вызовов
applythisконтекстный объектдаИнициализация свойств
alsoitконтекстный объектдаПобочные действия, логирование

Для конфигурации объекта без возврата нового значения используйте apply (доступ через this) или also (доступ через it). Для вычисления нового значения на основе объекта используйте let (доступ через it) или run (доступ через this). Для группировки вызовов без цепочки используйте with. Следование этой схеме делает код предсказуемым для других разработчиков, знакомых с Kotlin.

apply и also: конфигурация с возвратом объекта

apply — scope function, которая возвращает контекстный объект и предоставляет доступ к нему через this. Это идеальный инструмент для инициализации свойств объекта после создания. Внутри блока apply можно устанавливать свойства без повторения имени объекта. Метод возвращает сам объект, что позволяет встраивать apply в цепочки или использовать в инициализаторах.

kotlin
data class ServerConfig(
    var host: String = "localhost",
    var port: Int = 8080,
    var useTls: Boolean = false
)

val config = ServerConfig().apply {
    host = "api.example.com"
    port = 443
    useTls = true
}

В листинге apply конфигурирует ServerConfig, обращаясь к свойствам напрямую через this (this опущен). Без apply пришлось бы писать config.host = ..., config.port = ... — три повторяющихся ссылки на одну и ту же переменную. Возврат объекта позволяет сохранить результат конфигурации в val config. also, в отличие от apply, передаёт объект через it и подходит для побочных действий.

kotlin
fun saveUser(user: User) {
    validate(user).also { result ->
        println("Validation result: $result")
    }
    val savedUser = user.also {
        log("Saving user ${it.id}")
        database.save(it)
    }
}

В примере also используется для логирования — возвращая исходный объект, он выполняет побочное действие (вывод сообщения) без изменения объекта. Это цепочка: validate(user) возвращает User, also логирует результат и передаёт пользователя дальше. also также полезен для добавления элементов в коллекции или отладки в середине цепочки преобразований.

let и run: преобразования и вычисления

let — scope function, возвращающая результат лямбды и передающая контекст через it. Это основная функция для null-безопасных преобразований: если объект nullable, комбинация ?.let { } выполнит блок только при ненулевом значении. let также используется для ограничения области видимости временных переменных и map-подобных преобразований одного типа в другой.

kotlin
fun findUser(id: Int): User?

val displayName = findUser(42)?.let { user ->
    "${user.name} (${user.email})"
} ?: "Unknown user"

// let with collection transformation
val numbers = listOf("1", "2", "3")
val parsed = numbers.firstOrNull()?.let {
    it.toIntOrNull()
} ?: 0

В первом примере let вызывается через ?. — блок выполняется, только если findUser вернул не null. Лямбда принимает user через it (можно переименовать для ясности) и возвращает форматированную строку. Если пользователь не найден — Elvis-оператор ?: даёт значение по умолчанию. Во втором примере let преобразует строку в число с потенциальным null.

run — аналог let с доступом через this, возвращающий результат лямбды. run удобен для вычислений, где требуется контекст объекта, но возвращается новое значение. Вызов run() без объекта (как run { ... }) создаёт временную область видимости для переменных. with — не-расширение с доступом через this, подходящее для группировки вызовов без цепочки.

kotlin
// run as context-driven computation
val transformed = listOf(1, 2, 3).run {
    filter { it > 1 }.map { it * 10 }
}
println(transformed) // [20, 30]

// with for grouping calls
val info = with(StringBuilder()) {
    append("Name: ")
    append("Alice")
    toString()
}

В примере run на списке возвращает результат фильтрации и маппинга — само преобразование, а не список. with принимает StringBuilder как аргумент (не через точку) и внутри работает с this: append, append, toString. Результат with — строка info. Выбор между run и with часто субъективен, но with предпочтительнее, когда объект передаётся из другого места без цепочки вызовов.

with: контекст без расширения

with отличается от остальных scope functions тем, что не является функцией-расширением. Она принимает объект первым аргументом, а лямбду — вторым. Внутри лямбды объект доступен через this. with удобен для группировки вызовов методов объекта без сохранения результата или когда требуется временно «войти» в контекст для чтения нескольких свойств.

kotlin
val user = User("Alice", "alice@example.com", "Admin")

val summary = with(user) {
    "User $name has role $role and email $email"
}
println(summary)

// with for UI component initialization
with(TextView(context)) {
    text = "Hello"
    textSize = 18.0f
    setTextColor(Color.BLUE)
}

В первом примере with формирует строку summary, обращаясь к свойствам user напрямую через this. Без with пришлось бы писать "User ${user.name} has role ${user.role}" — многократное повторение user. Во втором примере with группирует конфигурацию TextView: все методы и свойства вызываются на одном объекте без повторения. Недостаток with — невозможность использовать в цепочке, так как он не является расширением и не возвращает контекстный объект.

Часто задаваемые вопросы

Какую scope function выбрать для инициализации объекта?

Используйте apply — он возвращает объект и предоставляет доступ через this, позволяя устанавливать свойства без повторения имени объекта. also подходит, если нужен явный параметр it для отличаемости от свойств объекта.

Чем let отличается от run?

let передаёт контекст через it (явный параметр), run — через this (неявный получатель). let удобен для null-проверок и преобразований, run — для вычислений с контекстом. Обе возвращают результат лямбды.

Когда применять with вместо apply?

with не является расширением — он принимает объект аргументом. Используйте with для группировки вызовов, когда не нужна цепочка. apply — расширение, возвращающее объект, удобное в инициализаторах и builder-паттернах.

Можно ли вложить одну scope function в другую?

Да, но будьте осторожны с this — вложенные this создают путаницу. Используйте let или also с явными именами для вложенных контекстов, чтобы избежать неоднозначности при обращении к внешнему объекту.

Какая scope function самая производительная?

Все scope functions — inline, поэтому они не создают накладных расходов на анонимные классы. Разница в производительности между ними пренебрежимо мала. Выбор должен основываться на читаемости и конвенциях, а не на скорости.

Итоги

  • Scope functions — пять inline-функций Kotlin (let, run, with, apply, also) для выполнения кода в контексте объекта
  • apply возвращает контекстный объект с доступом через this — идеален для инициализации свойств
  • also возвращает контекстный объект с доступом через it — подходит для логирования и побочных действий
  • let возвращает результат лямбды с доступом через it — основа для null-безопасных преобразований
  • run возвращает результат лямбды с доступом через this — удобен для вычислений с контекстом
  • with не является расширением и возвращает результат лямбды — подходит для группировки вызовов без цепочки
  • Комбинация признаков (this/it и возврат объекта/результата) определяет выбор scope function для каждой задачи

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также