expect-классы, свойства и фабрики
expect/actual работает не только для функций — но у классов есть нюансы, из-за которых чаще берут фабрики.
expect class — объявление класса в общем коде, тело которого (поля, конструкторы) полностью реализуется в
actual classна каждой платформе.
expect-свойства
Простейший случай после функций — свойство. Например, флаг отладки или путь к кэшу:
// commonMain
expect val cacheDir: String// androidMain
actual val cacheDir: String = appContext.cacheDir.absolutePathexpect-классы
Можно объявить целый класс, реализация которого платформенная:
// commonMain
expect class HttpEngineProvider() {
fun create(): HttpClientEngine
}На каждой платформе actual class HttpEngineProvider вернёт свой движок. Однако у expect class есть ограничения: его API должен совпадать на всех платформах, а лишние члены в actual просачиваются в общий код только через осторожные правила. Поэтому на практике для «создать платформенный объект» чаще выбирают фабричную функцию.
Почему фабрика часто лучше
Вместо expect class объявляют общий интерфейс и expect-функцию, которая его создаёт:
// commonMain
interface Database {
fun query(sql: String): List<Row>
}
expect fun createDatabase(): DatabaseПлатформа реализует actual fun createDatabase(), возвращая свой класс. Общий код работает с интерфейсом Database, а expect/actual касается только точки создания. Это даёт больше свободы: платформенные реализации могут отличаться внутренне как угодно, лишь бы удовлетворяли интерфейсу.
Как работает под капотом
Когда вы пишете expect class, компилятор требует, чтобы actual class на каждой платформе имел совпадающий публичный API: те же конструкторы, методы, свойства. Это жёстче, чем интерфейс, потому что класс — это и контракт, и реализация одновременно. Фабрика разделяет их: интерфейс — стабильный контракт в общем коде, actual-функция — гибкая точка сборки. Отсюда правило: expect class хорош, когда API действительно одинаков, а различается лишь «внутренность»; иначе берите интерфейс + фабрику.
Частые ошибки
Пытаться добавить в actual class публичный метод, которого нет в expect — и ожидать, что общий код его увидит. Общий код видит только то, что объявлено в expect. Вторая ошибка — злоупотребление expect class там, где хватило бы интерфейса, что усложняет рефакторинг.
Итоги
expect/actualприменимо к свойствам и целым классам, не только функциям.- У
expect classжёсткое требование совпадения API на всех платформах. - Интерфейс +
expect-фабрика часто гибче и читаемее. - Общий код видит только то, что объявлено в
expect.