Kotlin Academy · Lektion

expect/actual-mekanismen til platform-API'er

Deklarér forventede deklarationer i fælles kode, og angiv faktiske implementeringer pr. platform.

Lektion 2 af 413 trin

expect/actual-mekanismen til platform-API'er er en gratis Kotlin Academy-lektion på CoddyKit. Dette er lektion 2 af 4. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i Kotlin Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Kotlin Academy-kurset indeholder 4 lektioner i alt.

expect/actual-mønstret

expect erklærer en API-kontrakt i commonMain. Hver platform leverer en actual-implementering. Kompileren håndhæver, at alle målplatforme har en 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 erklærer en klasse med dens API i commonMain. Hver platform leverer den fulde implementering.

// 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 erklærer en singleton med sin kontrakt. Det er nyttigt til platformspecifikke hjælpefunktioner som logning, indstillinger eller analyse.

// 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 på øverste niveau

expect val og expect fun på øverste niveau er enkle og fungerer godt til tilstandsløse platformspecifikke hjælpefunktioner.

// 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 med standardimplementering

Fra og med Kotlin 1.9 kan expect fun have en standardimplementering via expect fun f() { }. Platformene kan tilsidesætte den eller bruge standarden.

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

Hvis en platform allerede har præcis den klasse, du har brug for, skal du bruge actual typealias til at pege på den i stedet for at indpakke den.

// 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 (indirekte mønster)

For større fleksibilitet skal du erklære en grænseflade i commonMain og kun bruge expect/actual til den fabrik, der opretter platforminstansen.

// 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 med expect/actual

I commonTest skal du teste den fælles API. De faktiske implementeringer køres under platformspecifikke testopgaver (testDebugUnitTest på Android, iosX64Test på iOS).

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

Almindelige faldgruber

Enhver erklæret expect skal have en actual i ALLE konfigurerede målplatforme, ellers mislykkes projektbygningen. Manglende actual-implementeringer opdages ved kompilering.

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

expect annotation class

expect annotation class muliggør platformspecifik tilknytning af annotationer — f.eks. tilknytning af en fælles annotation, der minder om @Parcelize, til Androids Parcelize-plugin.

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

Eksempel fra virkeligheden: Keychain/Preferences

Et typisk eksempel fra virkeligheden på expect/actual er en grænseflade til sikker lagring, hvor Android bruger EncryptedSharedPreferences, og iOS bruger 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
}

Hurtigt tjek

Hvad sker der ved kompilering, hvis en expect-erklæring ikke har en actual til en målplatform?

Opsummering

expect erklærer API'et i commonMain. actual leverer platformimplementeringen. Brug actual typealias, når platformen allerede har den nødvendige type. Manglende actual-implementeringer opdages ved kompilering — ingen overraskelser ved kørsel.

Gratis at komme i gang

Lær Kotlin med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
51
Lektioner
203

Ofte stillede spørgsmål

Er lektionen “expect/actual-mekanismen til platform-API'er” gratis?

Ja — alle 3 lektioner i læringssporet Kotlin Academy, inklusive “expect/actual-mekanismen til platform-API'er”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Kotlin Academy-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “expect/actual-mekanismen til platform-API'er”?

Deklarér forventede deklarationer i fælles kode, og angiv faktiske implementeringer pr. platform. Du øver dig i Kotlin Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på Kotlin Academy?

Der kræves ingen tidligere erfaring. Kotlin Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 2 af 4.

Hvor lang tid tager lektionen “expect/actual-mekanismen til platform-API'er”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne Kotlin Academy-lektion?

Ja. Alle Kotlin Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. KMP-projektstruktur: commonMain, androidMain, iosMain
  2. expect/actual-mekanismen til platform-API'er
  3. Deling af Repository- og Use Case-lag
  4. Dependency injection i KMP med Koin
← Tilbage til Kotlin Academy