Привет, Хабр!

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

Допустим, есть сервис логирования UserService, который нужно часто передавать во многие функции. Без контекстных параметров пришлось бы всюду писать fun outputMessage(users: UserService, msg: String). С параметрами контекста достаточно объявить функцию так:

interface UserService {
    fun log(message: String)
}

context(users: UserService)
fun outputMessage(message: String) {
    users.log("Log: $message")
}

context указывает, что функция outputMessage ожидает в своём окружении объект UserService. То есть при вызове компилятор неявно подставит нужный сервис из контекста.

Также параметры контекста можно применять и к свойствам. Например:

context(users: UserService)
val firstUser: String
    get() = users.findUserById(1)

Этот синтаксис делает firstUser ленивым свойством, которое берёт UserService из контекста и возвращает пользователя по ID.

Kotlin разрешает контекстные параметры по типу в месте вызова: достаточно положить объект нужного типа в контекст с помощью стандартных функций with/run/apply или специального блока context(obj) { ... }. Если в одном месте окажется несколько объектов с одинаковым типом, компилятор выдаст ошибку неоднозначности.

Но если вызвать функцию без необходимого контекста, Kotlin тоже выдаст ошибку компиляции, все context‑параметры обязаны быть обеспечены объектом нужного типа.

Можно использовать context(_ : Type), если нужен только тип в контексте, а не именованно к нему обращаться. Тогда внутри функции объект доступен, но по имени _ обращаться к нему нельзя.

На данный момент контекстные параметры всё ещё экспериментальны. Основные ограничения:

  • Конструкторы не могут иметь context‑параметров.

  • Свойства с context не могут иметь полей или начальных значений.

  • Свойства с context не могут использовать делегаты (by). То есть context может стоять только перед функцией или перед val/var без инициализатора.

Чтобы опробовать эту фичу в своём проекте, нужно включить флаг компилятора -Xcontext-parameters. Например, в Gradle‑файле можно добавить:

kotlin {
    compilerOptions {
        freeCompilerArgs.add("-Xcontext-parameters")
    }
}

Для наглядности ещё один пример: предположим, есть Logger и TransactionManager, которые часто используются вместе. С контекстными параметрами можно сделать так:

context(logger: Logger, tx: TransactionManager)
fun perform(data: Data) {
    logger.debug("Start processing $data")
    tx.run {
        // операции в транзакции
    }
    logger.debug("End")
}

fun main() {
    val data = Data()
    with(ConsoleLogger()) {
        with(TransactionManager()) {
            perform(data)  // Logger и TransactionManager предоставлены автоматически
        }
    }
}

Вызов функции perform внутри вложенных with блоков автоматически предоставляет и Logger, и TransactionManager: внутри функции сразу доступны оба контекста.

Без контекстных параметров пришлось бы прокидывать оба объекта во все вызовы. Причём если забыть обернуть вызов в необходимый context, компиляция не пройдёт — Kotlin потребует нужные объекты.

Подытожим: context‑параметры позволяют вынести общие зависимости (сервисы, менеджеры и тому подобное) из сигнатур функций, сделав код чище и короче. Они автоматически подставляются по типу, а сама механика упрощает внедрение зависимостей и дизайн DSL. Фича пока доступна через EAP, но обещано, что в следующих версиях Kotlin она будет стабилизирована.

Потянете курс Kotlin QA Engineer? Пройдите тест и узнаете
Потянете курс Kotlin QA Engineer? Пройдите тест и узнаете

Если вам интересно применить Kotlin не только в проде, но и в тестовой инфраструктуре, посмотрите курс Kotlin QA Engineer. Он про автотесты для UI, бизнес-логики и API на разных платформах, DI (Hilt) и Jetpack Compose, плюс встраивание тестов в CI/CD с упором на нагрузку, стиль и базовую безопасность.

Для знакомства с форматом обучения и экспертами приходите на бесплатные демо-уроки:

  • 15 января, 20:00. «Mutation Testing: как я узнал, что мои тесты с 95% coverage ничего не проверяют». Записаться

  • 21 января, 20:00. «Особенности Kotlin в UI и API тестировании». Записаться