0Pricing
Kotlin Academy · Lezione

Meccanismo expect/actual per le API di piattaforma

Dichiari le definizioni attese nel codice comune e fornisca implementazioni actual per ogni piattaforma.

Meccanismo expect/actual per le API di piattaforma è una lezione Kotlin Academy gratuita su CoddyKit. Questa è la lezione 2 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Kotlin Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Kotlin Academy include 4 lezioni in totale.

Il pattern expect/actual

expect dichiara un contratto API in commonMain. Ogni piattaforma fornisce un'implementazione actual. Il compilatore verifica che tutti i target dispongano di un 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 dichiara una classe con la relativa API in commonMain. Ogni piattaforma fornisce l'implementazione 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 dichiara un singleton con il relativo contratto. È utile per utility specifiche della piattaforma, come logging, impostazioni o analytics.

// 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 al livello superiore

expect val e expect fun al livello superiore sono semplici e funzionano bene per utility di piattaforma prive di stato.

// 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 implementazione predefinita

A partire da Kotlin 1.9+, expect fun può avere un'implementazione predefinita tramite expect fun f() { }. Le piattaforme possono sovrascriverla oppure utilizzare quella predefinita.

// 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 used

Actual Typealias

Se una piattaforma dispone già della classe esatta di cui ha bisogno, utilizzi actual typealias per riferirsi direttamente a essa invece di inserirla in un wrapper.

// 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 (pattern indiretto)

Per una maggiore flessibilità, dichiari un'interfaccia in commonMain e utilizzi expect/actual solo per la factory che crea l'istanza specifica della piattaforma.

// 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()

Test con expect/actual

In commonTest, testi l'API comune. Le implementazioni actual vengono eseguite durante i task di test specifici della piattaforma (testDebugUnitTest su Android, iosX64Test su iOS).

// commonTest:
import kotlin.test.Test
import kotlin.test.assertNotNull
class PlatformTest {
    @Test
    fun testUUID() {
        val uuid = generateUUID()
        assertNotNull(uuid)
        println("UUID: $uuid")
    }
}

Problemi comuni

Ogni expect dichiarato deve avere un actual in TUTTI i target configurati, altrimenti la build non va a buon fine. Gli actual mancanti vengono rilevati in fase di compilazione.

// 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") }

Classe di annotazione expect

expect annotation class consente di associare annotazioni specifiche della piattaforma; ad esempio, di associare un'annotazione comune simile a @Parcelize al plugin Parcelize di 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 CommonParcelize

Caso reale: Keychain/Preferences

Un tipico caso reale di expect/actual è un'interfaccia per l'archiviazione sicura, in cui Android utilizza EncryptedSharedPreferences e iOS utilizza 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
}

Verifica rapida

Che cosa accade in fase di compilazione se una dichiarazione expect non ha un actual per un target?

Riepilogo

expect dichiara l'API in commonMain. actual fornisce l'implementazione specifica della piattaforma. Utilizzi actual typealias quando la piattaforma dispone già del tipo necessario. Gli actual mancanti vengono rilevati in fase di compilazione: nessuna sorpresa a runtime.

Domande Frequenti

La lezione «Meccanismo expect/actual per le API di piattaforma» è gratuita?

Sì — il testo completo di «Meccanismo expect/actual per le API di piattaforma» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Kotlin Academy, passa a CoddyKit PRO. Il corso Kotlin Academy include 4 lezioni in totale.

Cosa imparerò in «Meccanismo expect/actual per le API di piattaforma»?

Dichiari le definizioni attese nel codice comune e fornisca implementazioni actual per ogni piattaforma. Eserciti Kotlin Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare Kotlin Academy?

Non è richiesta alcuna esperienza precedente. Kotlin Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 2 di 4.

Quanto tempo richiede la lezione «Meccanismo expect/actual per le API di piattaforma»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione Kotlin Academy?

Sì. Ogni lezione Kotlin Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Struttura di progetto KMP: commonMain, androidMain, iosMain
  2. Meccanismo expect/actual per le API di piattaforma
  3. Condividere i livelli Repository e Use Case
  4. Dependency injection in KMP con Koin
← Torna a Kotlin Academy