skip to content

What is the opt-in mechanism in Kotlin, and how do you consume an experimental API like ExperimentalCoroutinesApi?

level: juniorimportance: must knowfreq 55%

answer

  1. @RequiresOptIn declares, @OptIn consumes
  2. @OptIn is local, non-propagating
  3. Three options: @OptIn / propagate / -opt-in flag
  4. ExperimentalCoroutinesApi is a coroutines marker

basics

~10 s

Some Kotlin APIs are marked experimental and may change. The compiler warns or errors when you use them. To say 'I accept the risk', you add @OptIn(TheMarker::class) above your function or class.

solid answer

~40 s

Library authors mark unstable APIs with a custom annotation defined via @RequiresOptIn. Using such an API produces a compiler warning or error unless you explicitly accept it. The simplest way is to annotate the using declaration with @OptIn(ExperimentalCoroutinesApi::class). @OptIn is local and non-propagating: it accepts the API only for that declaration and does NOT force callers of your code to opt in. ExperimentalCoroutinesApi is a real marker in kotlinx.coroutines protecting APIs like runningReduce-style flow operators or TestCoroutineScheduler internals. Alternatives to @OptIn: re-annotate your own declaration with the same marker (which propagates the requirement to your callers), or opt in project-wide with the -opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi compiler flag (configured via compilerOptions { optIn.add(...) } in Gradle).

code

kotlin · 6 lines
kotlin
import kotlinx.coroutines.ExperimentalCoroutinesApi

@OptIn(ExperimentalCoroutinesApi::class)
fun consumeExperimentalCoroutineApi() {
    // experimental coroutines API call goes here
}

go deeper

for a junior

Knows experimental APIs need @OptIn(Marker::class) to silence the compiler warning/error.

for a middle

Distinguishes consuming (@OptIn) from declaring (@RequiresOptIn) and names the module-wide -opt-in flag.

for a senior

Explains the propagation alternative and when @OptIn vs re-annotation vs flag is appropriate.

for a principal

Frames opt-in as an API-evolution governance tool and reasons about its impact across a multi-module codebase.

## What problem does opt-in solve? Library authors sometimes want to ship an API that works but whose **signature or behavior might change** in a future release. They don't want callers to depend on it unknowingly. Kotlin's **opt-in requirement** mechanism lets an author flag such an API so that any use produces a compiler diagnostic until the caller explicitly acknowledges the risk. ## The two halves - **Declaring** the requirement: the author defines a marker annotation and tags it with `@RequiresOptIn`. Any API annotated with that marker becomes opt-in-required. - **Consuming** the API: the caller must do one of three things, or the compiler complains. ## Consuming options 1. **`@OptIn(Marker::class)`** on the using function/class/file. This is **local** and **non-propagating** — you accept the API here, and your own callers are unaffected. 2. **Propagate**: annotate your own declaration with the *same marker*. Now your declaration also requires opt-in, pushing the decision up to your callers. 3. **Module-wide**: pass `-opt-in=fully.qualified.Marker` to the compiler (Gradle: `kotlin { compilerOptions { optIn.add("...") } }`). Useful for accepting one marker across a whole module. ## `ExperimentalCoroutinesApi` example ```kotlin import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.flow @OptIn(ExperimentalCoroutinesApi::class) fun useExperimental() { // call into an API guarded by @ExperimentalCoroutinesApi } ``` `ExperimentalCoroutinesApi` is a marker annotation declared in kotlinx.coroutines; the library tags not-yet-stable coroutine/flow APIs with it. ## Key keywords `@RequiresOptIn` (declares), `@OptIn` (consumes locally), `-opt-in` / `optIn.add(...)` (module-wide), propagation by re-annotation.

  • Does @OptIn force callers of your function to also opt in?
    No. @OptIn is non-propagating — it accepts the API only for the annotated declaration; callers are unaffected. To push the requirement up, you must re-annotate with the marker itself.

@OptIn is like signing a waiver before a risky activity — you accept the risk personally, but you don't sign on behalf of everyone after you.

saying these in an interview costs you the question

  • Thinking @OptIn changes the experimental API's behavior rather than just silencing the diagnostic
  • Claiming @OptIn propagates the requirement to callers
  • Confusing @OptIn (consume) with @RequiresOptIn (declare)
  • Believing experimental APIs are broken or unusable rather than just unstable

context