skip to content

expect / actual Functions

An expect function is a signature with no body in common code, and every target must provide a matching actual or the build fails. That compiler-enforced completeness is the whole appeal over conditional compilation.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

questions

5

What are `expect` and `actual` functions in Kotlin Multiplatform, and how do you use them to expose platform-specific behavior through a common API?

level: juniorimportance: must knowfreq 70%

answer

  1. expect = bodiless declaration in commonMain
  2. actual = real body per target source set
  3. compiler: one actual per expect, every target
  4. signatures must match exactly
  5. defaults/type params only on expect side

basics

~20 s

In shared code you write expect fun with no body, like a promise. Each platform (Android, iOS, JVM, JS) provides the real actual fun. Common code calls the function without knowing which platform fills it in.

solid answer

~40 s

`expect`/`actual` is Kotlin Multiplatform's mechanism for declaring a single API in `commonMain` and supplying a per-target implementation. In `commonMain` you write a bodiless `expect fun platformName(): String`. In each target source set (`androidMain`, `iosMain`, `jvmMain`, etc.) you write `actual fun platformName(): String { ... }` with a real body. The signatures must match: same name, parameters, return type, and visibility. The compiler enforces that every `expect` has exactly one matching `actual` per target it compiles for. Common code calls `platformName()` directly and the linker binds the right `actual` per platform. This keeps business logic in shared code while delegating only the platform-touching parts (system clock, UUID, file paths) to native implementations.

code

kotlin · 10 lines
kotlin
// commonMain/Platform.kt
expect fun platformName(): String

fun greeting(): String = "Hello from " + platformName()

// androidMain/Platform.android.kt
actual fun platformName(): String = "Android"

// iosMain/Platform.ios.kt
actual fun platformName(): String = "iOS"

go deeper

for a junior

Can explain expect = declaration, actual = implementation, and write a basic pair.

for a middle

Knows the matching rules and that the compiler enforces one actual per target.

for a senior

Contrasts expect/actual with interface+DI and picks the right tool per use case.

for a principal

Reasons about API surface, evolution, and where platform seams belong in a large KMP module graph.

## The problem it solves Kotlin Multiplatform (KMP) lets you share Kotlin code across targets (Android/JVM, iOS/Native, JS, Wasm). Most logic is platform-agnostic, but some operations need platform APIs (e.g., generating a UUID, reading the current time, getting a device name). `expect`/`actual` lets the **common** code depend on a declaration while each **platform** supplies the implementation. ## How it works In `commonMain` you declare an **expected** function with **no body**: ```kotlin // commonMain expect fun platformName(): String ``` The `expect` keyword means "this exists; some target will provide it." In each platform source set you provide the **actual** function with the `actual` keyword and a real body: ```kotlin // androidMain actual fun platformName(): String = "Android " + android.os.Build.VERSION.SDK_INT // iosMain actual fun platformName(): String = platform.UIKit.UIDevice.currentDevice.systemName() ``` ## Matching rules The `actual` declaration must **structurally match** the `expect`: identical name, parameter list (types and order), return type, and compatible visibility/modifiers. Type parameters and default parameter values are declared **only on the `expect` side** — repeating a default value on `actual` is an error. ## Compiler enforcement The compiler verifies that **every `expect` has exactly one matching `actual`** in each target's compilation. A missing actual fails to compile; an extra or non-matching one also fails. This guarantee is the whole point: you can never ship a target that forgot to implement a shared declaration. ## Where it fits vs. alternatives `expect`/`actual` is one way to inject platform code. The other common approach is a plain **interface in common + per-platform implementation provided via DI or a factory**. Use `expect`/`actual` for thin, leaf-level platform calls; prefer interfaces when you want testability/mocking or multiple implementations. ## Key keywords/APIs - `expect` — declares the contract in `commonMain`. - `actual` — supplies the body per target source set (`androidMain`, `iosMain`, `jsMain`, …). - Hierarchical source sets (e.g., a shared `nativeMain`) can host one `actual` for several native targets.

  • What happens if you forget the `actual` for one target?
    That target fails to compile with an error saying the expected declaration has no actual; common and the other targets are unaffected.
  • Can common code see the platform-specific return value type?
    Only the declared return type from the `expect` signature. Common code can't reference platform-only types unless they're surfaced through the common signature.

expect is a job posting in the shared office; each branch office (platform) must hire someone (actual) who exactly fits the description.

saying these in an interview costs you the question

  • Thinking `expect` functions can have a body in commonMain
  • Putting the implementation in commonMain instead of a platform source set
  • Claiming the actual can have a different signature
  • Believing it's a runtime reflection mechanism rather than compile-time binding

context

open as a page

What are the exact matching rules between an `expect fun` and its `actual fun`? Where do default parameter values and type parameters go?

level: middleimportance: must knowfreq 55%

basics

~20 s

The actual function must have the same name, the same parameters and return type, and at least the same visibility. Default values and type parameters are written only on the expect side, not repeated on actual.

open as a page

How does the compiler decide which `actual fun` to use, and how do hierarchical/intermediate source sets affect where you can place a single `actual`?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Each platform build pulls together commonMain plus that platform's source sets. The compiler picks the actual from whichever source set is included for that target. With intermediate source sets like nativeMain, one actual can serve several targets at once.

open as a page

When should you choose `expect`/`actual` functions versus a common interface with per-platform implementations? What are the trade-offs, including testability and inlining?

level: seniorimportance: should knowfreq 35%

basics

~10 s

Use expect/actual for small, fixed platform calls where there's exactly one implementation per platform. Use a common interface when you want to swap or mock implementations, inject dependencies, or have more than one variant.

open as a page

What constraints and pitfalls apply to `expect`/`actual` functions around `suspend`, return-type variance across platforms, and evolving the API over time?

level: principalimportance: nice to knowfreq 18%

basics

~20 s

If the expect function is suspend, every actual must be suspend too. The common return type must be something all platforms can satisfy. Changing an expect signature forces updating every actual, so design these boundaries carefully.

open as a page