skip to content

@OptIn / @RequiresOptIn

@RequiresOptIn marks an API as unstable and @OptIn acknowledges the risk at the use site, with propagation or a compiler flag as alternatives. Interviewers use it to check you treat experimental APIs as a deliberate decision rather than a warning to silence.

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

questions

5

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

open as a page

How do you author an experimental API with @RequiresOptIn, and what do its level and message parameters control?

level: middleimportance: should knowfreq 40%

basics

~20 s

You make your own annotation and mark it with @RequiresOptIn. Then you put that annotation on the API you want to flag. You can set a message and choose whether use is a warning or an error.

open as a page

How do you opt in to an experimental marker for an entire module via the compiler, and what are the trade-offs versus per-declaration @OptIn?

level: middleimportance: should knowfreq 30%

basics

~20 s

You can tell the compiler to accept a marker everywhere in the module using a build setting instead of writing @OptIn on every spot. It's convenient but you lose the per-use record of where the risky API is used.

open as a page

Contrast propagating an opt-in requirement (re-annotating with the marker) versus using @OptIn locally. When would you choose each?

level: seniorimportance: should knowfreq 35%

basics

~10 s

Re-annotating with the marker passes the 'please opt in' requirement on to whoever calls your code. Using @OptIn stops the requirement at your code — your callers don't have to do anything.

open as a page

You're designing the public API of a widely-used Kotlin library. How would you use @RequiresOptIn to manage API evolution and stability tiers, and what pitfalls would you guard against?

level: principalimportance: nice to knowfreq 18%

basics

~20 s

Use opt-in markers to label which parts of your API are still experimental, so users must consciously accept the risk. Keep stable APIs unmarked, and have a clear plan for graduating experimental APIs to stable.

open as a page