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

В чем разница между expect/actual и обычным интерфейсом?

2.0 Middle🔥 92 комментариев
#Kotlin основы#Многомодульность

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

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

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

Разница между expect/actual и обычными интерфейсами

Основное различие между expect/actual механизмом и обычными интерфейсами заключается в их предназначении и области применения. Это два разных инструмента из арсенала Kotlin, решающих принципиально разные задачи.

📐 Обычные интерфейсы - для полиморфизма

Обычные интерфейсы — это фундаментальный механизм объектно-ориентированного программирования, который позволяет определять контракты (наборы методов и свойств) без конкретной реализации. Их ключевые особенности:

// Обычный интерфейс
interface NetworkClient {
    fun sendRequest(url: String): Response
    val isConnected: Boolean
}

// Имплементация для Android
class AndroidNetworkClient(private val context: Context) : NetworkClient {
    override fun sendRequest(url: String): Response {
        // Используем Android-специфичные API
        val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE)
        // ... реализация для Android
    }
    
    override val isConnected: Boolean
        get() = // проверка соединения для Android
}

// Имплементация для iOS (в общем случае - другой модуль)
class IosNetworkClient : NetworkClient {
    override fun sendRequest(url: String): Response {
        // Используем iOS-специфичные API
        // ... реализация для iOS
    }
    
    override val isConnected: Boolean
        get() = // проверка соединения для iOS
}

Ключевые аспекты обычных интерфейсов:

  • Полиморфизм: Разные классы реализуют один интерфейс
  • Выбор реализации во время исполнения: Реализация определяется в runtime
  • Используются внутри одной платформы/модуля: Все реализации компилируются вместе
  • Не требуют специальной поддержки компилятора: Стандартная языковая конструкция

🌉 Expect/Actual - для мультиплатформенной разработки

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

// Общий модуль (commonMain) - объявляем expect-часть
expect class PlatformFileSystem() {
    fun readFile(path: String): ByteArray
    fun writeFile(path: String, content: ByteArray)
}

// Android модуль (androidMain) - предоставляем actual-реализацию
actual class PlatformFileSystem {
    actual fun readFile(path: String): ByteArray {
        // Используем java.io.File для Android
        return File(path).readBytes()
    }
    
    actual fun writeFile(path: String, content: ByteArray) {
        File(path).writeBytes(content)
    }
}

// iOS модуль (iosMain) - другая actual-реализация
actual class PlatformFileSystem {
    actual fun readFile(path: String): ByteArray {
        // Используем iOS Foundation API
        return NSData.dataWithContentsOfFile(path)?.toByteArray() ?: throw IOException()
    }
    
    actual fun writeFile(path: String, content: ByteArray) {
        // iOS-специфичная реализация
    }
}

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

  • Компиляция для разных платформ: Каждая actual-реализация компилируется только для своей платформы
  • Выбор реализации во время компиляции: Правильная версия выбирается на этапе сборки
  • Проверка на этапе компиляции: Компилятор гарантирует, что все expect-декларации имеют actual-реализации
  • Автоматическое разрешение зависимостей: Gradle автоматически подключает нужные реализации

🎯 Сравнительная таблица

АспектExpect/ActualОбычные интерфейсы
НазначениеКроссплатформенная разработкаПолиморфизм, абстракция
Область примененияKotlin MultiplatformЛюбой Kotlin проект
Время выбора реализацииКомпиляцияВыполнение
Требует ли специальной структуры проектаДа (мультиплатформенный проект)Нет
Можно ли иметь несколько реализаций на одной платформеНет (одна actual на платформу)Да (сколько угодно классов)
Доступ к платформ-специфичным APIПрямой в actual-реализацияхТолько через общие интерфейсы

💡 Когда что использовать

Используйте expect/actual, когда:

  • Пишете мультиплатформенный код на Kotlin
  • Необходим прямой доступ к платформ-специфичным API (Android SDK, iOS Foundation, JavaScript DOM)
  • Хотите гарантировать наличие реализаций для всех целевых платформ на этапе компиляции

Используйте обычные интерфейсы, когда:

  • Нужен полиморфизм внутри одной платформы
  • Требуется возможность подмены реализации в runtime
  • Пишете модульные тесты с mock-объектами
  • Создаете плагинную архитектуру

🔄 Комбинированный подход

В реальных мультиплатформенных проектах часто комбинируют оба подхода:

// Общий модуль
expect class PlatformNotificationManager() {
    fun showNotification(title: String, message: String)
}

interface NotificationStrategy {
    fun shouldShowNotification(): Boolean
    fun getPriority(): Int
}

// Android модуль
actual class PlatformNotificationManager {
    private val strategy: NotificationStrategy = DefaultNotificationStrategy()
    
    actual fun showNotification(title: String, message: String) {
        if (strategy.shouldShowNotification()) {
            // Android-специфичная реализация с использованием strategy
        }
    }
}

⚠️ Важные ограничения

expect/actual имеет существенные ограничения:

  • Одна actual-реализация на платформу: Нельзя иметь две разные actual-реализации в одном модуле
  • Нет полиморфизма actual-типов: Нельзя передавать Android actual-тип в код, ожидающий iOS actual-тип
  • Ограниченный набор языковых конструкций: Можно использовать только с классами, функциями, свойствами, объектами

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