Kotlin 标准库提供了一种机制,用于要求并明确同意使用 API 的某些元素。 通过这种机制,库开发人员可以将使用其 API 需要选择加入的特定条件告知用户, 例如,如果某个 API 处于实验状态,并且将来可能会更改。
为了避免潜在的问题,编译器会向此类 API 的用户发出警告, 告知他们这些条件,并要求他们在使用 API 之前选择加入。
如果库作者将一个库的 API 声明标记为*要求选择加入*, 你应该明确同意在代码中使用它。 有多种方式可以选择加入使用此类 API,所有方法均不受技术限制。 你可以自由选择最适合自己的方式。
在使用供第三方(库)使用的 API 时,你也可以把其选择加入的要求传播到自己的 API。 为此,请在你的 API 主体声明中标注*要求选择加入的注解*。 这可以让你使用要求选择加入的 API 元素。
// 库代码
@RequiresOptIn(message = "This API is experimental. It may be changed in the future without notice.")
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
annotation class MyDateTime // 要求选择加入的注解
@MyDateTime
class DateProvider // 要求选择加入的类
// 客户端代码
fun getYear(): Int {
val dateProvider: DateProvider // 错误:DateProvider 要求选择加入
// ...
}
@MyDateTime
fun getDate(): Date {
val dateProvider: DateProvider // OK:该函数也要求选择加入
// ...
}
fun displayDate() {
println(getDate()) // 错误:getDate() 要求选择加入
}
如本例所示,带注释的函数看起来是 @MyDateTime
API 的一部分。
因此,这种选择加入会将选择加入的要求传播到客户端代码;其客户将看到相同的警告消息,
并且也必须同意。
Implicit usages of APIs that require opt-in also require opt-in. If an API element doesn't have an opt-in requirement annotation but its signature includes a type declared as requiring opt-in, its usage will still raise a warning. See the example below.
// Client code
fun getDate(dateProvider: DateProvider): Date { // Error: DateProvider requires opt-in
// ...
}
fun displayDate() {
println(getDate()) // Warning: the signature of getDate() contains DateProvider, which requires opt-in
}
要使用多个需要选择加入的API,请在声明中标记所有需要选择加入的注解。
在不公开其自身API的模块(例如应用程序)中,你可以选择使用 API 而无需将选择加入的要求传播到代码中。
这种情况下,请使用 @OptIn
标记你的声明,
并以要求选择加入的注解作为参数:
// 库代码
@RequiresOptIn(message = "This API is experimental. It may be changed in the future without notice.")
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
annotation class MyDateTime // 要求选择加入的注解
@MyDateTime
class DateProvider // 要求选择加入的类
//客户端代码
@OptIn(MyDateTime::class)
fun getDate(): Date { // 使用 DateProvider;不传播选择加入的要求
val dateProvider: DateProvider
// ...
}
fun displayDate() {
println(getDate()) // OK:不要求选择加入
}
当有人调用函数 getDate()
时,不会通知他们函数主体中使用的选择加入 API 的要求。
Note that if @OptIn
applies to the declaration whose signature contains a type declared as requiring opt-in,
the opt-in will still propagate:
// Client code
@OptIn(MyDateTime::class)
fun getDate(dateProvider: DateProvider): Date { // Has DateProvider as a part of a signature; propagates the opt-in requirement
// ...
}
fun displayDate() {
println(getDate()) // Warning: getDate() requires opt-in
}
要在一个文件的所有函数和类中使用要求选择加入的 API,请在文件的顶部,
文件包说明和导入声明前添加文件级注释 @file:OptIn
。
//客户端代码
@file:OptIn(MyDateTime::class)
The
-opt-in
compiler option is available since Kotlin 1.6.0. For earlier Kotlin versions, use-Xopt-in
.
{type="note"}
如果你不想在使用要求选择加入 API 的每个地方都添加注解,则可以为整个模块选择加入这些 API。
要选择在模块中使用 API,请使用参数 -opt-in
进行编译,
使用 -opt-in = org.mylibrary.OptInAnnotation
指定该 API 使用的要求选择加入注解的标准名称。
使用此参数进行编译的效果与模块中每个声明都有注解 @OptIn(OptInAnnotation::class)
的效果相同。
如果使用 Gradle 构建模块,可以添加如下参数:
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
// ...
tasks.named<KotlinCompilationTask<*>>("compileKotlin").configure {
compilerOptions.freeCompilerArgs.add("-opt-in=org.mylibrary.OptInAnnotation")
}
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
// ...
tasks.named('compileKotlin', KotlinCompilationTask) {
compilerOptions {
freeCompilerArgs.add("-opt-in=org.mylibrary.OptInAnnotation")
}
}
如果你的 Gradle 模块是多平台模块,请使用 optIn
方法:
sourceSets {
all {
languageSettings.optIn("org.mylibrary.OptInAnnotation")
}
}
sourceSets {
all {
languageSettings {
optIn('org.mylibrary.OptInAnnotation')
}
}
}
对于 Maven,它将是:
<build>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>...</executions>
<configuration>
<args>
<arg>-opt-in=org.mylibrary.OptInAnnotation</arg>
</args>
</configuration>
</plugin>
</plugins>
</build>
要在模块级别选择加入多个 API,请为每个要求选择加入的 API 添加以上描述的参数之一。
如果想获得使用者使用你的模块 API 的明确同意,请创建一个注解类,作为_要求选择加入的注解_。 这个类必须使用 @RequiresOptIn 注解:
@RequiresOptIn
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
annotation class MyDateTime
要求选择加入的注解必须满足以下几个要求:
选择加入的要求可以具有以下两个严格级别之一:
RequiresOptIn.Level.ERROR
。选择加入是强制性的。 否则,使用标记 API 的代码将无法编译。 默认级别。RequiresOptIn.Level.WARNING
。选择加入不是强制性的,而是建议使用的。 没有它,编译器会发出警告。
要设置所需的级别,请指定 @RequiresOptIn
注解的 level
参数。
另外,你可以提供一个 message
来通知用户有关使用该 API 的特定条件。
编译器会将其显示给使用该 API 但未选择加入的用户。
@RequiresOptIn(level = RequiresOptIn.Level.WARNING, message = "This API is experimental. It can be incompatibly changed in the future.")
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
annotation class ExperimentalDateTime
如果你发布了多个需要选择加入的独立功能,请为每个功能声明一个注解。 这使你的用户可以更安全地使用 API:他们只能使用其明确接受的功能。 这也使你可以独立地从功能中删除选择加入的要求。
要在使用 API 时要求选择加入,请给它的声明添加要求选择加入的注解。
@MyDateTime
class DateProvider
@MyDateTime
fun getTime(): Time {}
Note that for some language elements, an opt-in requirement annotation is not applicable:
- You cannot annotate a backing field or a getter of a property, just the property itself.
- You cannot annotate a local variable or a value parameter.
如果要求选择加入尚未稳定的特性,请仔细处理 API 由实验状态到稳定状态的转换, 以避免破坏客户端代码。
当稳定前 API 稳定之后并以稳定状态发布后,请从声明中删除其要求选择加入的注解。 客户端将可以不受限制地使用它们。但是,你应该将注解类留在模块中,以便与现有的客户端代码保持兼容。
为了让 API 用户相应地更新其模块(从代码中删除注解并重新编译),
请将注解标记为 @Deprecated
并在弃用 message 中提供说明。
@Deprecated("This opt-in requirement is not used anymore. Remove its usages from your code.")
@RequiresOptIn
annotation class ExperimentalDateTime