Mecanismo expect/actual para API de plataforma
Declare declaraciones esperadas en el código común y proporcione implementaciones actualizadas para cada plataforma.
Mecanismo expect/actual para API de plataforma es una lección gratuita de Kotlin Academy en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Kotlin Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Kotlin Academy incluye 4 lecciones en total.
El patrón expect/actual
expect declara un contrato de API en commonMain. Cada plataforma proporciona una implementación actual. El compilador garantiza que todos los destinos tengan una implementación actual.
// commonMain:
expect fun platformName(): String
// androidMain:
actual fun platformName(): String = "Android ${android.os.Build.VERSION.SDK_INT}"
// iosMain:
actual fun platformName(): String = UIDevice.currentDevice.systemName()expect class
expect class declara una clase con su API en commonMain. Cada plataforma proporciona la implementación completa.
// commonMain:
expect class DateTime {
fun format(pattern: String): String
val timestamp: Long
}
// androidMain:
actual class DateTime {
actual val timestamp = System.currentTimeMillis()
actual fun format(pattern: String): String =
java.text.SimpleDateFormat(pattern).format(java.util.Date(timestamp))
}expect object
expect object declara un singleton con su contrato. Resulta útil para utilidades específicas de la plataforma, como el registro, la configuración o las analíticas.
// commonMain:
expect object Logger {
fun debug(msg: String)
fun error(msg: String, cause: Throwable? = null)
}
// androidMain:
actual object Logger {
actual fun debug(msg: String) = android.util.Log.d("App", msg)
actual fun error(msg: String, cause: Throwable?) =
android.util.Log.e("App", msg, cause)
}expect val / fun de nivel superior
Los expect val y expect fun de nivel superior son sencillos y funcionan bien para utilidades de plataforma sin estado.
// commonMain:
expect val isDebugBuild: Boolean
expect fun generateUUID(): String
// androidMain:
actual val isDebugBuild: Boolean = BuildConfig.DEBUG
actual fun generateUUID(): String = java.util.UUID.randomUUID().toString()
// iosMain:
actual val isDebugBuild: Boolean = false // or a native flag
actual fun generateUUID(): String = NSUUID().UUIDString()expect fun con implementación predeterminada
Desde Kotlin 1.9+, expect fun puede tener una implementación predeterminada mediante expect fun f() { }. Las plataformas pueden sobrescribirla o utilizar la predeterminada.
// commonMain (Kotlin 1.9+):
expect fun log(msg: String) {
println("[DEFAULT] $msg") // used if no actual overrides
}
// androidMain:
actual fun log(msg: String) = android.util.Log.d("KMP", msg)
// iosMain (uses default):
// no actual needed — default is usedAlias de tipo actual
Si una plataforma ya cuenta con la clase exacta que necesita, utilice actual typealias para referenciarla en lugar de envolverla.
// commonMain:
expect class AtomicInt {
fun get(): Int
fun incrementAndGet(): Int
}
// androidMain / jvmMain:
actual typealias AtomicInt = java.util.concurrent.atomic.AtomicInteger
// iosMain:
actual class AtomicInt {
private var value = 0
actual fun get() = value
actual fun incrementAndGet() = ++value
}expect interface (patrón indirecto)
Para obtener más flexibilidad, declare una interfaz en commonMain y utilice expect/actual únicamente para la factoría que crea la instancia específica de la plataforma.
// commonMain:
interface FileStorage {
suspend fun readFile(path: String): ByteArray
suspend fun writeFile(path: String, data: ByteArray)
}
expect fun createFileStorage(): FileStorage
// androidMain:
actual fun createFileStorage(): FileStorage = AndroidFileStorage()
// iosMain:
actual fun createFileStorage(): FileStorage = IosFileStorage()Pruebas con expect/actual
En commonTest, pruebe la API común. Las implementaciones actuales se ejecutan durante las tareas de prueba específicas de cada plataforma (testDebugUnitTest en Android, iosX64Test en iOS).
// commonTest:
import kotlin.test.Test
import kotlin.test.assertNotNull
class PlatformTest {
@Test
fun testUUID() {
val uuid = generateUUID()
assertNotNull(uuid)
println("UUID: $uuid")
}
}Errores comunes
Cada expect declarado debe tener un actual en TODOS los destinos configurados; de lo contrario, la compilación falla. Las implementaciones actual ausentes se detectan durante la compilación.
// If you add a new target (e.g., jvmMain), you must add:
// actual fun platformName(): String = "JVM"
// Otherwise:
// error: Expected function platformName has no actual declaration in module :shared for JVM
fun main() { println("Missing actuals = compile error") }Clase de anotación expect
expect annotation class permite asignar anotaciones específicas de la plataforma; por ejemplo, asignar una anotación común similar a @Parcelize al complemento Parcelize de Android.
// commonMain:
expect annotation class CommonParcelize()
// androidMain:
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY)
actual annotation class CommonParcelize
// iosMain:
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY)
actual annotation class CommonParcelizeCaso real: Keychain/Preferences
Un caso habitual de expect/actual en proyectos reales es una interfaz de almacenamiento seguro: Android utiliza EncryptedSharedPreferences e iOS utiliza Keychain.
// commonMain:
expect class SecureStorage {
fun save(key: String, value: String)
fun get(key: String): String?
}
// androidMain:
actual class SecureStorage {
private val prefs = /* EncryptedSharedPreferences */ mapOf<String,String>()
actual fun save(key: String, value: String) { /* ... */ }
actual fun get(key: String): String? = null // simplified
}Comprobación rápida
¿Qué ocurre durante la compilación si una declaración expect no tiene un actual para un destino?
Repaso
expect declara la API en commonMain. actual proporciona la implementación específica de la plataforma. Utilice actual typealias cuando la plataforma ya tenga el tipo necesario. Las implementaciones actual ausentes se detectan durante la compilación, sin sorpresas en tiempo de ejecución.
Preguntas frecuentes
¿La lección «Mecanismo expect/actual para API de plataforma» es gratis?
Sí — el texto completo de «Mecanismo expect/actual para API de plataforma» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Kotlin Academy, actualiza a CoddyKit PRO. El curso de Kotlin Academy incluye 4 lecciones en total.
¿Qué aprenderé en «Mecanismo expect/actual para API de plataforma»?
Declare declaraciones esperadas en el código común y proporcione implementaciones actualizadas para cada plataforma. Practicas Kotlin Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar Kotlin Academy?
No se requiere experiencia previa. Kotlin Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.
¿Cuánto tiempo toma la lección «Mecanismo expect/actual para API de plataforma»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de Kotlin Academy?
Sí. Cada lección de Kotlin Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Estructura de proyectos KMP: commonMain, androidMain, iosMain
- Mecanismo expect/actual para API de plataforma
- Compartir las capas de repositorio y casos de uso
- Inyección de dependencias en KMP con Koin