← Назад к вопросам

В чем разница между функциями run, let, also, apply?

1.0 Junior🔥 251 комментариев
#Kotlin основы

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Разница между run, let, also, apply в Kotlin

Эти четыре функции являются scope functions (функциями области видимости) в Kotlin, которые позволяют выполнять блок кода в контексте определенного объекта. Несмотря на схожесть, каждая имеет свои особенности в поведении и возвращаемом значении.

Основные отличия

ФункцияКонтекстный объект (this/it)Возвращаемое значениеТипичное применение
letit (явный параметр)Результат лямбда-выраженияПреобразование данных, проверка на null
runthis (неявный)Результат лямбда-выраженияИнициализация и вычисление результата
alsoit (явный параметр)Исходный объектДополнительные действия, логирование
applythis (неявный)Исходный объектКонфигурация объекта

Детальное рассмотрение каждой функции

1. let - преобразование с явным контекстом

val result = nullableString?.let { 
    // 'it' содержит nullableString, если она не null
    it.length // возвращается последнее выражение (Int)
}

Ключевые особенности: -[x] Использует it для доступа к контекстному объекту -[x] Возвращает результат выполнения лямбды -[x] Идеальна для null-safe операций и преобразований данных -[x] Позволяет переименовать it для лучшей читаемости: nullableString?.let { str -> ... }

Пример использования:

user?.let { 
    println("Processing user: ${it.name}")
    it.name.length // возвращаем длину имени
}

2. run - выполнение с неявным контекстом

val result = someObject.run {
    // 'this' содержит someObject, доступ к свойствам без префикса
    property = "value" // модификация свойства
    computeResult() // возвращается последнее выражение
}

Ключевые особенности: -[x] Использует this для доступа к контекстному объекту -[x] Возвращает результат выполнения лямбды -[x] Может использоваться как с объектом, так и без (какая-то форма run без receiver) -[x] Полезен для инициализации с вычислением результата

Пример использования:

val formattedDate = Date().run {
    val formatter = SimpleDateFormat("yyyy-MM-dd")
    formatter.format(this) // возвращаем отформатированную строку
}

3. also - дополнительные действия

val modifiedObject = originalObject.also {
    // 'it' содержит originalObject
    println("Object before modification: $it")
    // Можно выполнять побочные действия
}.apply {
    // Дальнейшая модификация...
}

Ключевые особенности: -[x] Использует it для доступа к контекстному объекту -[x] Возвращает исходный объект (а не результат лямбды) -[x] Предназначена для побочных эффектов: логирование, отладка, валидация -[x] Часто используется в цепочках вызовов

Пример использования:

val list = mutableListOf<Int>().also {
    println("Creating list with capacity 10")
}.apply {
    addAll(listOf(1, 2,的小孩3))
}

4. apply - конфигурация объекта

val configuredObject = SomeClass().apply {
    // 'this' содержит новый экземпляр SomeClass
    property1 = "value1" // настройка свойств
    property2 = 42
    // Неявный возврат this
}

Ключевые особенности: -[x] Использует this для доступа к контекстному объекту -[x] Возвращает исходный объект после его конфигурации -[x] Идеальна для инициализации и настройки объектов (Builder-паттерн) -[x] Не требует явного возврата значения

Пример использования:

val button = Button(context).apply {
    text = "Submit"
    textSize = 16f
    setOnClickListener { /* обработка клика */ }
    background = ColorDrawable(Color.BLUE)
}

Практические рекомендации по выбору

  1. Используйте let когда:

    • Работаете с nullable-переменными
    • Нужно преобразовать объект в другой тип
    • Требуется явное именование контекстного объекта для ясности
  2. Используйте run когда:

    • Нужен доступ к свойствам объекта без префикса it.
    • Требуется инициализировать объект и сразу получить результат вычислений
    • Выполняете вычисления, где контекстный объект используется многократно
  3. Используйте also когда:

    • Нужно выполнить дополнительные действия (логирование, валидацию) без изменения объекта
    • Требуется сохранить цепочку вызовов, возвращая исходный объект
    • Дебаггинг или логирование промежуточных состояний
  4. Используйте apply когда:

    • Конфигурируете объект после создания (аналог Builder-паттерна)
    • Инициализируете свойства нового объекта
    • Работаете с объектами, которые требуют много настроек

Пример комбинированного использования

data class Person(var name: String = "", var age: Int = 0, var city: String = "")

fun createPerson(): Person {
    return Person().apply {
        name = "Alex"
        age = 30
    }.also {
        println("Person created: $it")
    }.run {
        if (city.isEmpty()) {
            city = "Moscow" // устанавливаем город по умолчанию
        }
        this // явно возвращаем this, хотя в apply это неявно
    }
}

Производительность и читаемость

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

Главное правило: выбирайте функцию, которая наиболее точно отражает ваше намерение: -LET для преобразования или null-safe операций -RUN для вычислений с контекстом -ALSO для побочных действий -APPLY для конфигурации объектов