skip to content

expect / actual Classes & Objects

expect class declares a type whose members each target must supply, matching constructors and supertypes. Interviewers may note its Beta status and ask what you would do instead — usually an interface plus a factory function.

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

questions

5

In Kotlin Multiplatform, what does declaring `expect class Platform` in common code and `actual class Platform` in a platform source set mean? Give a minimal example.

level: juniorimportance: must knowfreq 65%

answer

  1. expect = signatures only, no body
  2. actual = one implementation per target
  3. compile-time binding, no runtime interface
  4. every member needs actual, including constructor
  5. lives in commonMain vs platform source sets

basics

~20 s

You write a placeholder class in shared code that says 'this exists, here's its shape.' Each platform (Android, iOS) then provides the real version with the same name and members. Shared code uses it without knowing the platform.

solid answer

~40 s

`expect class Platform` is a declaration in the `commonMain` source set with no body for its members — it states the contract (constructors, methods, properties) that every target must satisfy. Each platform source set (e.g. `androidMain`, `iosMain`) supplies an `actual class Platform` with a matching signature, implemented using platform-specific APIs (java.lang on JVM, Foundation on iOS). The compiler links each `expect` to exactly one `actual` per target at compile time — there is no runtime indirection or interface. Common code calls `Platform()` and gets the right implementation depending on which target you compile. The `actual` must match the `expect` exactly: same name, package, type parameters, and member signatures. This is the core mechanism letting one shared API surface have N platform-specific bodies.

code

kotlin · 12 lines
kotlin
// commonMain
expect class Platform() {
    val name: String
}

// jvmMain
actual class Platform actual constructor() {
    actual val name: String = System.getProperty("os.name")
}

// commonMain usage
fun describe() = "Running on ${Platform().name}"

go deeper

for a junior

Can state expect is the shared signature and actual is the per-platform implementation, and write a trivial matching pair.

for a middle

Explains compile-time binding, the one-actual-per-target rule, and that common code only sees expect members.

for a senior

Contrasts with interfaces/DI, notes expect object, and discusses where actuals live across source sets and the constructor actualization.

for a principal

Frames it as a build-graph contract, weighs expect/actual vs typealias-to-platform-type or interface+factory for API stability and binary compatibility.

## What expect/actual solves Kotlin Multiplatform (KMP) lets you share code across targets (JVM/Android, iOS/native, JS, Wasm). Most code is shared, but some needs a **platform-specific implementation** (e.g. reading the OS name, a UUID, a crypto primitive). The `expect`/`actual` mechanism is Kotlin's way to declare an API in shared code and bind a real implementation per target. ## expect = the contract In `commonMain` you write: ```kotlin // commonMain expect class Platform() { val name: String fun greet(): String } ``` An **expected** declaration has no implementation — only signatures. It is the *promise* that an actual exists for every target. ## actual = the implementation Each platform source set provides the body: ```kotlin // androidMain actual class Platform actual constructor() { actual val name: String = "Android " + android.os.Build.VERSION.SDK_INT actual fun greet(): String = "Hi from $name" } // iosMain actual class Platform actual constructor() { actual val name: String = platform.UIKit.UIDevice.currentDevice.systemName() actual fun greet(): String = "Hi from $name" } ``` Every member that was expected must be marked `actual`, including the constructor (`actual constructor()`). ## Key facts - **Compile-time binding**: the compiler matches each `expect` to one `actual` per target. No reflection, no runtime dispatch, no generated interface. - **One actual per target**: missing an actual for any compiled target is a compile error. - **Common code is target-agnostic**: it just calls `Platform().greet()`. - **expect object** works too: `expect object Config` matched by `actual object Config`. - Members can also be **optionally** actualized by inheritance or by `actual typealias` (a sibling topic), but the direct form is a matching `actual class`. ## Why not just an interface? An interface needs a runtime implementation chosen by you; expect/actual is resolved by the build target, so shared code references a concrete type (`Platform`) with zero ceremony and full inlining/value-class friendliness.

  • What happens if you forget to provide an actual for one of the compiled targets?
    It is a compile-time error for that target's compilation — the build fails until every expected declaration has a matching actual.
  • Can common code see the platform-specific fields of the actual class?
    No. Common code only sees what the expect declares. Extra members added to an actual are invisible from commonMain.

expect is a job description posted to shared code; each platform hires its own employee (actual) that must match the description exactly.

saying these in an interview costs you the question

  • Thinking expect/actual is resolved at runtime via reflection or DI
  • Believing the actual can have a different name or package than the expect
  • Saying expect classes need a body in commonMain
  • Confusing it with regular interface/abstract-class polymorphism
  • Forgetting the constructor itself needs to be actualized

context

open as a page

What are the matching rules between an `expect class` and its `actual class`? Cover constructors, members, supertypes, and the case where the actual carries members the expect did not declare.

level: middleimportance: must knowfreq 50%

basics

~20 s

The actual must have the same name, package, and matching constructors and members the expect declared, each marked actual. The actual is allowed to add extra members and extra supertypes, but those extras are invisible from shared code.

open as a page

Why does the Kotlin compiler emit a 'Expected classes are in Beta' warning, and what are the practical risks and alternatives when using `expect class`?

level: middleimportance: should knowfreq 35%

basics

~20 s

Expected classes are still marked Beta, so Kotlin warns you their behavior may change in future versions. For stable public APIs, many teams prefer matching a platform type with actual typealias or using interface + factory instead, reserving expect class for app-internal code.

open as a page

In a hierarchical KMP project, an `expect class` defined in `commonMain` fails to compile for one target with 'expected declaration has no actual'. Walk through the likely causes and how source-set hierarchy and intermediate source sets affect where the actual may live.

level: seniorimportance: should knowfreq 22%

basics

~20 s

Usually one target has no matching actual. In hierarchical projects the actual can live in an intermediate source set (like appleMain shared by iOS targets), but every leaf target must still be covered. Check package/name match, that all members are actual, and that the source set is on that target's compilation.

open as a page

Compare `expect object` with `expect class`, and explain when you would choose an expect class over an interface-plus-factory seam for a shared API.

level: seniorimportance: should knowfreq 28%

basics

~20 s

An expect object is a single shared singleton with one actual object per platform; an expect class can be instantiated many times. Use expect class when you need a concrete shared type with value/identity semantics; use an interface plus factory when you only need behavior and want a stable, mockable seam.

open as a page