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

Где будешь объявлять actual-декларации?

2.4 Senior🔥 151 комментариев
#Kotlin основы#Многомодульность

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

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

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

Объявление actual-деклараций в Kotlin Multiplatform

Actual-декларации являются ключевой частью архитектуры Kotlin Multiplatform (KMP) и объявляются в platform-specific модулях, которые соответствуют целевым платформам проекта. Их расположение строго определяется структурой KMP и служит для предоставления конкретных реализаций для объявленных в общем модуле expect-деклараций.

Структура модулей Kotlin Multiplatform и место actual-деклараций

В типичном KMP-проекте структура модулей выглядит следующим образом:

my-multiplatform-project/
├── shared/                      # Общий (common) модуль
│   ├── src/
│   │   ├── commonMain/          # Здесь объявляются expect−декларации
│   │   │   └── kotlin/
│   │   └── androidMain/        # НЕ здесь! Это не платформенный модуль.
│   └── build.gradle.kts
├── androidApp/                  # Android−специфичный модуль
│   ├── src/
│   │   └── main/                # Здесь объявляются actual−декларации для Android
│   │       └── kotlin/
│   └── build.gradle.kts
├── iosApp/                      # iOS−специфичный модуль
│   ├── src/
│   │   └── main/                # Здесь объявляются actual−декларации для iOS
│   │       └── kotlin/
│   └── build.gradle.kts

Actual-декларации должны быть размещены в отдельных Gradle-модулях, предназначенных для конкретной платформы (например, androidApp, iosApp), а не в директориях androidMain, iosMain внутри общего модуля (shared). Эти androidMain/iosMain используются для платформенно-специфичного кода, который уже находится в общем модуле (например, для реализации actual интерфейсов или классов, объявленных как expect в commonMain), но сама структура модулей должна быть разделена.

Пример объявления actual-декларации для Android

Рассмотрим конкретный пример. В общем модуле (shared/src/commonMain/kotlin/) мы объявляем expect-функцию для получения текущего времени:

// В shared/src/commonMain/kotlin/TimeHelper.kt
package com.example.common

expect fun getCurrentTimeMillis(): Long

Теперь, чтобы предоставить реализацию для Android, мы создаем отдельный Android-модуль (androidApp) и объявляем actual-декларацию в его исходном коде:

// В androidApp/src/main/kotlin/TimeHelper.kt
package com.example.common

import java.lang.System

actual fun getCurrentTimeMillis(): Long {
    return System.currentTimeMillis()
}

Ключевые правила и лучшие практики

  • Строгое соответствие имен и пакетов: Имя actual-декларации и её пакет должны полностью совпадать с именем и пакетом соответствующей expect-декларации. Kotlin компилятор использует это для связывания.
  • Одна actual-декларация для каждой целевой платформы: Для каждой expect-декларации в общем модуле необходимо предоставить точно одну actual-декларацию в каждом модуле целевой платформы, которая будет использовать этот общий код.
  • Разделение модулей vs разделение исходных наборов: Важно понимать разницу:
    *   **Разделение исходных наборов (source sets)** внутри общего модуля (`commonMain`, `androidMain`, `iosMain`) используется, когда весь multiplatform-код находится в одном Gradle-модуле. `actual`-декларации в этом случае пишутся в `androidMain/iosMain`.
    *   **Разделение модулей (separate Gradle modules)** — более чистая и масштабируемая архитектура для полноценных приложений. `actual`-декларации объявляются тогда в этих отдельных модулях (`androidApp/src/main`, `iosApp/src/main`).
  • Проверка зависимостей: В build.gradle.kts платформенного модуля (androidApp) обязательно должна быть объявлена зависимость на общий модуль (shared).
// В androidApp/build.gradle.kts
dependencies {
    implementation(project(":shared"))
}

Типичные ошибки в размещении

  • Объявление actual в commonMain: Это приведет к ошибке компиляции, так как commonMain не имеет доступа к платформенным API.
  • Объявление actual в androidMain общего модуля, когда используется архитектура с раздельными модулями: Компилятор не найдет реализации, так как будет искать её в соответствующем платформенном модуле.
  • Несовпадение пакетов: Самая частая причина ошибки No actual implementation found for expect declaration.

Таким образом, actual-декларации объявляются в модулях, специфичных для целевой платформы (androidApp, iosApp), в их основной исходной директории (src/main/kotlin/), соблюдая полное соответствие имени и пакета с expect-декларацией из общего модуля. Это обеспечивает четкое разделение логики, корректную компиляцию и масштабируемость проекта Kotlin Multiplatform.