In Kotlin 1.3.70, the @RequiresOptIn and @OptIn annotations replaced the older @Experimental and @UseExperimental markers, while the -Xopt-in compiler flag took the place of -Xuse-experimental.
The Kotlin standard library includes a mechanism to enforce explicit consent for using specific API elements. Library developers can flag APIs that require users to opt in—for example, experimental features that may undergo breaking changes in future versions. To prevent unintended issues, the compiler issues warnings to users of these APIs, notifying them of the associated conditions and requiring explicit opt-in before use.
Choosing to Use Opt-In APIs
When a library author marks an API element as requiring opt-in, you must explicitly agree to use it in your code. There are multiple ways to opt in, with no technical restrictions—choose the method that best fits your workflow.
Propagating Opt-In Requirements
If you're building an API that third-party consumers will use, you can propagate the opt-in requirement from a dependent library to your own API. To do this, annotate your API declaration with the same opt-in requirement annotation used by the library. This lets you use the marked API element while passing the opt-in requirement along to your own users.
@RequiresOptIn(message = "This API is experimental and may be modified or removed without prior notice.")
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
annotation class ExperimentalTimeTools // Opt-in required annotation
@ExperimentalTimeTools
class DateTimeGenerator // Class requiring opt-in
fun retrieveYear(): Int {
val generator: DateTimeGenerator // Error: DateTimeGenerator requires opt-in
// ...
}
@ExperimentalTimeTools
fun fetchCurrentDate(): Date {
val generator = DateTimeGenerator() // OK: function inherits opt-in requirement
// ...
}
fun showDate() {
println(fetchCurrentDate()) // Error: fetchCurrentDate() requires opt-in
}
Functions annotated with the opt-in marker are treated as part of the experimental API surface. This means your own API consumers will see the same warnings and must also opt in. For multiple opt-in required APIs, apply all relevant annotations to you're declaration.
Non-Propagating Opt-In Usage
In modules that don't expose public APIs (such as end-user applications), you can use opt-in required APIs without passing the requirement to other parts of your code. Use the @OptIn annotation on your declaration, specifying the required opt-in annotation as its argument.
@RequiresOptIn(message = "This experimental API may change in future versions.")
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
annotation class ExperimentalTimeTools
@ExperimentalTimeTools
class DateTimeGenerator
@OptIn(ExperimentalTimeTools::class)
fun fetchCurrentDate(): Date {
val generator = DateTimeGenerator() // Use experimental API without propagating requirement
// ...
}
fun showDate() {
println(fetchCurrentDate()) // OK: no opt-in required for callers
}
Callers of fetchCurrentDate() won't receive any notifications about the experimental API used inside the function.
To use an opt-in required API across an entire file, add the file-level @file:OptIn annotation at the top of the file, before the package declaration and imports:
@file:OptIn(ExperimentalTimeTools::class)
Module-Wide Opt-In
If you don't want to add annotations everywhere you use opt-in required APIs, you can enable opt-in for an entire module. To do this, use the -Xopt-in compiler flag with the fully qualified name of the opt-in requirement annotation. This is equivalent to adding @OptIn(AnnotationName::class) to every declaration in the module.
For Gradle projects using Groovy DSL:
tasks.withType(KotlinCompile).configureEach {
kotlinOptions {
freeCompilerArgs += "-Xopt-in=com.example.library.ExperimentalTimeTools"
}
}
For Gradle projects using Kotlin DSL:
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions.freeCompilerArgs += "-Xopt-in=com.example.library.ExperimentalTimeTools"
}
For multi-platform Gradle modules using Groovy:
sourceSets {
all {
languageSettings {
useExperimentalAnnotation('com.example.library.ExperimentalTimeTools')
}
}
}
For multi-paltform Gradle modules using Kotlin DSL:
sourceSets {
all {
languageSettings.useExperimentalAnnotation("com.example.library.ExperimentalTimeTools")
}
}
For Maven projects:
<build>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>...</executions>
<configuration>
<args>
<arg>-Xopt-in=com.example.library.ExperimentalTimeTools</arg>
</args>
</configuration>
</plugin>
</plugins>
</build>
To opt in to multiple APIs at the module level, add a separate -Xopt-in flag (or useExperimentalAnnotation call) for each required annotation.
Requiring Opt-In for Your API
To request explicit consent from users of your module's API, create a custom annotation marked with @RequiresOptIn:
@RequiresOptIn
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
annotation class ExperimentalTimeTools
Opt-in requirement annotations must meet the following criteria:
- Have
BINARYretention policy - Not target
EXPRESSIONorFILE - Have no parameters
You can set one of two strictness levels for the opt-in requirement:
RequiresOptIn.Level.ERROR: Opt-in is mandatory; code using the marked API will fail to compile without explicit consent. This is the default level.RequiresOptIn.Level.WARNING: Opt-in is recommended but not mandatory; the compiler will issue a warning if the API is used without opting in.
To set the level and add a user-facing message:
@RequiresOptIn(
level = RequiresOptIn.Level.WARNING,
message = "This experimental time API may change incompatibly in future releases."
)
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
annotation class ExperimentalTimeTools
For multiple independent experimental features, create a separate opt-in annotation for each. This lets users opt in only to the features they need and allows you to stabilize each feature independently.
Marking API Elements
To require opt-in for an API element, apply your custom opt-in annotation to its declaration:
@ExperimentalTimeTools
class DateTimeGenerator
@ExperimentalTimeTools
fun getCurrentTimestamp(): Long { /* ... */ }
Transitioning to Stable APIs
When an experimental API reaches stable status and is released without opt-in requirements, remove the opt-in annotation from the API declaration. Users will then be able to use it without restrictions. However, you should keep the opt-in annotation class in your module to maintain backward compatibility with existing client code.
To guide users to update their code, mark the annotation as @Deprecated with a clear message:
@Deprecated("This opt-in requirement is no longer needed. Please remove all references to it in your code.")
@RequiresOptIn
annotation class ExperimentalTimeTools
Experimental Status of the Opt-In Mechanism
The opt-in requirement mechanism itself was experimental in Kotlin 1.3, meaning it could undergo breaking changes in future versions. When using @OptIn and @RequiresOptIn, the compiler may issue a warning:
This class can only be used with the compiler argument '-Xopt-in=kotlin.RequiresOptIn'
To eliminate this warning, add the compiler flag -Xopt-in=kotlin.RequiresOptIn to your build configuration.