Подключение к платформно-специфичным API
Функция
expect/actualв настоящее время находится на стадии Бета. Все языковые и инструментальные возможности, описанные в этом документе, могут быть изменены в будущих версиях Kotlin.
Если вы разрабатываете многоплатформенное приложение, которому необходимо получить доступ к платформно-специфичным API, реализующим требуемую функциональность, используйте механизм Kotlin ожидаемых и фактических объявлений.
С помощью этого механизма общий набор исходных кодов определяет ожидаемое объявление, а наборы исходных кодов платформ должны предоставить фактическое объявление, соответствующее ожидаемому объявлению. Это работает для большинства объявлений Kotlin, таких как функции, классы, интерфейсы, перечисления, свойства и аннотации.
//Common expect fun randomUUID(): String
//Android import java.util.* actual fun randomUUID() = UUID.randomUUID().toString()
//iOS import platform.Foundation.NSUUID actual fun randomUUID(): String = NSUUID().UUIDString()
Вот еще один пример совместного использования кода и взаимодействия между общим и платформным кодом в минималистичной фреймворке для ведения журнала.
//Common
enum class LogLevel {
DEBUG, WARN, ERROR
}
internal expect fun writeLogMessage(message: String, logLevel: LogLevel)
fun logDebug(message: String) = writeLogMessage(message, LogLevel.DEBUG)
fun logWarn(message: String) = writeLogMessage(message, LogLevel.WARN)
fun logError(message: String) = writeLogMessage(message, LogLevel.ERROR)
Оно ожидает, что целевые платформы предоставят платформно-специфические реализации для writeLogMessage, и общий код теперь может использовать это объявление без учета того, как оно реализовано.
//JVM
internal actual fun writeLogMessage(message: String, logLevel: LogLevel) {
println("[$logLevel]: $message")
}
Для JavaScript доступен совершенно другой набор API, и объявление actual будет выглядеть так.
//JS
internal actual fun writeLogMessage(message: String, logLevel: LogLevel) {
when (logLevel) {
LogLevel.DEBUG -> console.log(message)
LogLevel.WARN -> console.warn(message)
LogLevel.ERROR -> console.error(message)
}
}
Основные правила относительно ожидаемых и фактических объявлений:
- Ожидаемое объявление помечено ключевым словом
expect; фактическое объявление помечено ключевым словомactual. -
Объявления
expectиactualимеют одинаковое имя и расположены в одном пакете (имеют одинаковое полное квалифицированное имя). -
Объявления
expectникогда не содержат кода реализации.
Во время каждой компиляции платформы компилятор гарантирует, что каждое объявление, помеченное ключевым словом expect в общем или промежуточном наборе исходных кодов, имеет соответствующие объявления, помеченные ключевым словом actual во всех наборах исходных кодов платформ. Среда разработки предоставляет инструменты, которые помогут вам создать недостающие фактические объявления.
Если у вас есть платформно-специфичная библиотека, которую вы хотите использовать в общем коде, при этом предоставляя собственную реализацию для другой платформы, вы можете предоставить typealias существующему классу в качестве фактического объявления:
expect class AtomicRef<V>(value: V) {
fun get(): V
fun set(value: V)
fun getAndSet(value: V): V
fun compareAndSet(expect: V, update: V): Boolean
}
actual typealias AtomicRef<V> = java.util.concurrent.atomic.AtomicReference<V>
Рекомендуется использовать ожидаемые и фактические объявления только для объявлений Kotlin, которые имеют зависимость от платформы. Лучше реализовывать как можно больше функциональности в общем модуле, даже если это займет больше времени.
Не злоупотребляйте ожидаемыми и фактическими объявлениями — в некоторых случаях интерфейс может быть лучшим выбором, потому что он более гибкий и проще в тестировании.
© 2010–2020 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/reference/mpp-connect-to-apis.html