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

Какие знаешь особенности expect/actual?

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

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

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

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

Особенности expect/actual в Kotlin Multiplatform

expect/actual — это ключевой механизм Kotlin Multiplatform (KMP) для реализации платформно-специфичного кода, сохраняя общий интерфейс. Он позволяет декларировать ожидаемое поведение в общем модуле (expect) и предоставлять конкретные реализации для каждой целевой платформы (actual).

Основные принципы и синтаксис

Expect декларация располагается в общем модуле (commonMain) и определяет контракт без реализации:

// В модуле commonMain
expect class PlatformLogger {
    fun log(message: String)
}

expect fun getCurrentTime(): Long

Actual реализация предоставляется в платформных модулях (androidMain, iosMain, etc.):

// В модуле androidMain
actual class PlatformLogger {
    actual fun log(message: String) {
        Log.d("App", message) // Используем Android-specific API
    }
}

actual fun getCurrentTime(): Long {
    return System.currentTimeMillis() // Android реализация
}

Ключевые особенности и правила

  1. Строгая симметрия: Каждой expect декларации должна соответствовать одна actual реализация для каждой целевой платформы. Имена, модификаторы и сигнатуры должны полностью совпадать.

  2. Платформная изоляция: expect часть не может содержать реализацию — только объявление. Это гарантирует, что общий модуль остается чистым от платформного кода.

  3. Область применения: Можно использовать для:

    • Классы (обычные, интерфейсы, объекты)
    • Функции (включая top-level и членов классов)
    • Свойства (с возможностью указания custom getter/setter в actual)
    • Аннотации
  4. Модификаторы видимости: actual реализации могут расширять видимость, но не сокращать. Например, expect internal класс может быть реализован как actual public на конкретной платформе.

  5. Инициализация actual классов: Для expect class можно указать конструкторы в expect части, но их реализация будет в actual.

// commonMain
expect class FileHandler(path: String) {
    fun read(): String
}

// androidMain
actual class FileHandler actual constructor(actual val path: String) {
    actual fun read(): String {
        return File(path).readText()
    }
}

Практические примеры использования в Android контексте

1. Работа с системными API

// commonMain
expect fun shareText(text: String)

// androidMain
actual fun shareText(text: String) {
    val intent = Intent(Intent.ACTION_SEND)
    intent.type = "text/plain"
    intent.putExtra(Intent.EXTRA_TEXT, text)
    startActivity(Intent.createChooser(intent, null))
}

2. Платформные утилиты

// commonMain
expect object PlatformUtils {
    val isNetworkAvailable: Boolean
}

// androidMain
actual object PlatformUtils {
    actual val isNetworkAvailable: Boolean
        get() {
            val connManager = getSystemService<ConnectivityManager>()
            return connManager.activeNetworkInfo?.isConnected ?: false
        }
}

Преимущества и ограничения

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

  • Чистая архитектура: Общий код полностью абстрагирован от платформных деталей.
  • Безопасность: Компилятор проверяет соответствие expect/actual, предотвращая ошибки.
  • Поддержка инструментов: IDE (IntelliJ IDEA) предоставляет удобную навигацию между expect и actual.

Ограничения и нюансы:

  • Не для всех случаев: Для простых случаев часто используют expected/actual, но для сложных многоплатформных структур иногда применяют интерфейсы с платформными внедрениями через DI.
  • Ограниченная рефлексия: actual типы могут иметь ограничения в платформно-специфичной рефлексии.
  • Тестирование: Тесты для common модуля могут использовать только expect части, что требует особых подходов к тестированию мультиплатформного кода.

Best Practices для Android разработчиков

  • Минимизация expect деклараций: Использовать только для действительно необходимого платформного кода. Стараться максимально выносить логику в common модуль.
  • Единые контракты: Определять четкие интерфейсы в expect, не допуская их изменения на разных платформах.
  • Логирование архитектуры: При сложных мультиплатформных проектах создавать маппинг между expect и actual для документации.
// Пример документации в коде
/**
 * expect: Общий контракт для получения устройства
 * actual Android: Использует Build.MODEL + другие Android API
 * actual iOS: Использует UIDevice.current.model
 */
expect fun getDeviceModel(): String

expect/actual — это фундаментальный инструмент Kotlin Multiplatform, который позволяет Android разработчикам создавать кроссплатформные решения, сохраняя возможность использовать всю мощь Android-specific API в соответствующих реализациях. Правильное использование этого механизма — ключ к созданию maintainable и scalable мультиплатформных проектов.

Какие знаешь особенности expect/actual? | PrepBro