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

Что такое with {}?

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

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

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

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

Что такое with {} в Kotlin?

with — это стандартная функция-расширение (standard library function) в Kotlin, предназначенная для выполнения блока кода в контексте переданного объекта. Её основная цель — упрощение работы с объектом, позволяя опускать его имя при многократном обращении к его свойствам и методам внутри определённой области видимости.

Синтаксис и принцип работы

fun <T, R> with(receiver: T, block: T.() -> R): R {
    return receiver.block()
}

Ключевые особенности:

  • Принимает два аргумента: объект-получатель (receiver) и лямбда-выражение с расширяемым получателем (block: T.() -> R).
  • Возвращает результат выполнения лямбды (тип R). Это может быть любое значение, включая Unit.
  • Внутри лямбды объект receiver становится неявным получателем (this). Это позволяет обращаться к его членам (полям, методам) без повторного указания имени переменной.

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

Рассмотрим классический пример настройки UI-компонента без with и с ним.

Без with (многословный код):

val textView = TextView(context)
textView.text = "Hello, World!"
textView.textSize = 16f
textView.setPadding(10, 5, 10, 5)
textView.setOnClickListener { /* ... */ }

С with (более лаконичный и сгруппированный код):

val textView = with(TextView(context)) {
    text = "Hello, World!" // this.text = ...
    textSize = 16f // this.textSize = ...
    setPadding(10, 5, 10, 5) // this.setPadding(...)
    setOnClickListener { /* ... */ }
    // Возвращаем настроенный объект
    this
}
// `textView` теперь содержит полностью сконфигурированный TextView

В этом примере все операции над TextView собраны в одном логическом блоке, что улучшает читаемость.

Отличия от похожих функций apply, run, also

with часто путают с другими scope-функциями. Вот их ключевые различия:

  1. with vs apply:
    *   `with` **не является функцией-расширением** и принимает объект первым параметром.
    *   `with` **возвращает результат лямбды** (часто `Unit` или результат последней операции).
    *   `apply` — это функция-расширение (`receiver.apply { }`) и **всегда возвращает сам объект-получатель** (`receiver`). Идеально для цепочки вызовов (builder pattern).

```kotlin
// with - возвращает результат
val textLength = with(textView) {
    text = "Hello"
    text.length // возвращаемое значение
}

// apply - возвращает сам объект
val configuredTextView = textView.apply {
    text = "Hello"
    textSize = 16f
}.also { it.requestFocus() } // можно продолжить цепочку
```

2. with vs run:

    *   `run` — это функция-расширение (`receiver.run { }`).
    *   `run` также возвращает результат лямбды.
    *   Выбор между ними часто вопрос стиля. `with` удобен, когда объект уже создан, `run` — когда нужно проверить объект на `null` или выполнить код в контексте результата вычисления.

```kotlin
// run как функция-расширение
val result = textView.run {
    text = "Hi"
    text.length
}

// run с проверкой на null
nullableTextView?.run {
    text = "Safe call"
}
```

Преимущества и недостатки with

Преимущества (+):

  • Улучшает читаемость: Группирует операции над одним объектом в логический блок.
  • Уменьшает дублирование кода: Избавляет от многократного повторения имени переменной.
  • Позволяет вычислять результат: Можно вернуть итоговое значение на основе объекта, а не сам объект.

Недостатки/Ограничения (-):

  • Не является функцией-расширением: Нельзя использовать в цепочке вызовов (chaining) как apply или also.
  • Меньше безопасности с null: В отличие от run или let, with не предоставляет безопасного вызова (?.) по умолчанию.
  • Может ухудшить читаемость при вложенности: Если внутри лямбды with используется другая scope-функция, код может стать сложным для восприятия.

Рекомендации по использованию

  • Используйте with для настройки объектов или вычисления значения, когда вам не нужна цепочка вызовов.
  • Выбирайте apply, если ваша цель — инициализировать объект и вернуть его (паттерн Builder).
  • Используйте run для проверки на null или когда работаете с результатом вычисления.
  • Избегайте глубокой вложенности scope-функций (with(..){ apply{ run{ ... } } }).

Практический пример: Работа с коллекциями

data class Person(val name: String, var age: Int)

val people = listOf(Person("Alice", 29), Person("Bob", 31))

// Использование with для работы со списком
val summary = with(people) {
    """
    Количество людей: ${size}
    Имена: ${joinToString { it.name }}
    Средний возраст: ${map { it.age }.average()}
    """.trimIndent()
}
println(summary)
// Вывод:
// Количество людей: 2
// Имена: Alice, Bob
// Средний возраст: 30.0

В этом примере with позволяет удобно работать со свойствами и методами списка people (size, joinToString, map) без постоянных обращений к переменной.

Таким образом, with {} — это инструмент для организации кода, который выполняет серию операций над конкретным объектом в ограниченной области видимости, делая код внутри блока более лаконичным и фокусируясь на действиях, а не на повторяющемся имени объекта.