0Pricing
Kotlin Academy · Lekcja

Mechanizm expect/actual dla API platform

Deklaruj oczekiwane deklaracje we wspólnym kodzie i dostarczaj implementacje actual dla poszczególnych platform.

Mechanizm expect/actual dla API platform to bezpłatna lekcja Kotlin Academy na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Kotlin Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Kotlin Academy zawiera 4 lekcji w sumie.

Wzorzec expect/actual

expect deklaruje kontrakt interfejsu API w commonMain. Każda platforma udostępnia implementację actual. Kompilator wymaga, aby wszystkie targety miały implementację 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 deklaruje klasę wraz z jej interfejsem API w commonMain. Każda platforma udostępnia pełną implementację.

// 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 deklaruje singleton wraz z jego kontraktem. Jest przydatny w przypadku narzędzi specyficznych dla platformy, takich jak rejestrowanie, ustawienia czy analityka.

// 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 na najwyższym poziomie

Elementy expect val i expect fun na najwyższym poziomie są proste i dobrze sprawdzają się w bezstanowych narzędziach platformowych.

// 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 z implementacją domyślną

Od wersji Kotlin 1.9+ element expect fun może mieć implementację domyślną za pośrednictwem expect fun f() { }. Platformy mogą ją zastąpić lub użyć implementacji domyślnej.

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

Jeśli platforma ma już dokładnie taką klasę, jakiej potrzebujesz, użyj actual typealias, aby się do niej odwołać zamiast tworzyć opakowanie.

// 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 (wzorzec pośredni)

Aby uzyskać większą elastyczność, zadeklaruj interfejs w commonMain i użyj expect/actual wyłącznie dla fabryki tworzącej instancję platformową.

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

Testowanie z expect/actual

W commonTest należy testować wspólny interfejs API. Implementacje actual są uruchamiane podczas zadań testowych specyficznych dla platformy (testDebugUnitTest na Androidzie, iosX64Test na iOS).

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

Typowe problemy

Każda zadeklarowana konstrukcja expect musi mieć konstrukcję actual we WSZYSTKICH skonfigurowanych targetach, w przeciwnym razie kompilacja się nie powiedzie. Brakujące implementacje actual są wykrywane podczas kompilacji.

// 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 umożliwia mapowanie adnotacji specyficznych dla platformy — na przykład mapowanie wspólnej adnotacji podobnej do @Parcelize na wtyczkę Android Parcelize.

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

Praktyczny przykład: Keychain/Preferences

Typowym praktycznym zastosowaniem expect/actual jest interfejs bezpiecznego przechowywania danych, w którym Android używa EncryptedSharedPreferences, a iOS — 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
}

Szybkie sprawdzenie

Co dzieje się podczas kompilacji, jeśli deklaracja expect nie ma konstrukcji actual dla danego targetu?

Podsumowanie

expect deklaruje interfejs API w commonMain. actual udostępnia implementację platformową. Używaj actual typealias, gdy platforma ma już wymagany typ. Brakujące implementacje actual są wykrywane podczas kompilacji — nie ma niespodzianek w czasie działania.

Często zadawane pytania

Czy lekcja „Mechanizm expect/actual dla API platform” jest bezpłatna?

Tak — pełny tekst „Mechanizm expect/actual dla API platform” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Kotlin Academy, przejdź na CoddyKit PRO. Kurs Kotlin Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Mechanizm expect/actual dla API platform”?

Deklaruj oczekiwane deklaracje we wspólnym kodzie i dostarczaj implementacje actual dla poszczególnych platform. Ćwiczysz Kotlin Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Kotlin Academy?

Nie wymagamy żadnego doświadczenia. Kotlin Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.

Ile czasu zajmuje lekcja „Mechanizm expect/actual dla API platform”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Kotlin Academy?

Tak. Każda lekcja Kotlin Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Struktura projektu KMP: commonMain, androidMain, iosMain
  2. Mechanizm expect/actual dla API platform
  3. Współdzielenie warstw Repository i Use Case
  4. Wstrzykiwanie zależności w KMP za pomocą Koin
← Powrót do Kotlin Academy