持续创作,加速生长!这是我参与「日新计划 10 月更文挑战」的第15天,点击检查活动详情

前语

之前在给公司项目封装库的时分,领导告诉我封装的漂亮一点,等今后公司发展起来了可能需求把这个库提供给第三方接入运用。

此刻,就有这么一个问题:某些功用函数运用条件比较严苛,直接运用可能会呈现意想不到的结果,假如想要运用,需求结合其他状况判断是否能够运用。

为了避免第三方接入时误操作,我为这个运用条件严苛的函数别的封装了一个能够直接运用的新函数。

可是,即使如此,出于测试和维护需求,我也不能移除或者将原函数设置为私有(private)函数。

那么问题来了,我要怎样避免其他同事或者第三方在运用时不会误调用这个函数,一起又能在知晓直接运用可能导致的结果时依旧能够运用呢?

靠文档声明?明显这是不可靠的,就算你在文档中大写加粗标红着重这类函数的危险性,依旧会有人视若无睹。

其时我在谷歌苦苦搜索了好久,终于在 Kotlin 官方文档中找到一个完美契合我的需求的功用,那就是 Opt-in 。

其时关于 Opt-in 的材料,除了官方文档几乎没有其他材料,我也没有在实践中见到有什么库或者程序运用这个功用,所以我用起来仍是觉得心里发怵。

直到今年我开始触摸了 Compose ,我才发现,本来 Compose 中现已很多应用了这个功用:

使用 Kotlin 的 Opt-in (选择加入)功能注解API提示当前非稳定API

使用 Kotlin 的 Opt-in (选择加入)功能注解API提示当前非稳定API

而且在今年的年中发布的 Kotlin 1.7.0 中,该功用终于发布了正式版别。

所以,是时分介绍一下这个功用了。

正文

什么是 Opt-in

根据官方文档介绍。

Opt-in 是 Kotlin 规范库中的一个办法,用于声明运用某些 API 需求清晰的赞同。该功用能够让开发者告知 API 运用者运用某些 API 需求一些特定的条件,假如运用者现已知晓则需求清晰声明依旧运用(Opt-in)才能继续运用该 API。

例如,某些 API 尚处于测试阶段,未来可能会发生变化;亦或是我前语中说到的场景,都非常适合运用该办法。

假如咱们声明了某个办法(functiuon)或类(class)需求 Opt-in ,则IDE或编译器会发出正告,要求运用者清晰标示需求运用(Opt-in)。

怎么运用

在介绍怎样编写 Opt-in 注解之前,咱们先简单介绍一下怎么运用。

这儿咱们就以 Compose 中 LazyColunmstickyHeader 函数举例,咱们不需求关心这个函数的详细实现,只需求知道这个函数被符号为了 Opt-in :

@ExperimentalFoundationApi
fun stickyHeader(
    key: Any? = null,
    contentType: Any? = null,
    content: @Composable LazyItemScope.() -> Unit
)
@RequiresOptIn(
    "This foundation API is experimental and is likely to change or be removed in the " +
        "future."
)
annotation class ExperimentalFoundationApi

其中 ExperimentalFoundationApi 即用于符号需求挑选参加的注解名称。

能够看到, stickyHeader 被加上了 @ExperimentalFoundationApi 的注解。

此刻,假如咱们直接调用 stickyHeader ,将会收到如下的 IDE 错误:

使用 Kotlin 的 Opt-in (选择加入)功能注解API提示当前非稳定API

假如咱们无视这个正告,直接编译的话也会编译犯错。

为了消除这个正告,咱们能够挑选加上注解: @OptIn(ExperimentalFoundationApi::class) 表明咱们已知晓运用 stickyHeader 的危险,而且依旧需求运用。

加上上述注解后,错误消失,也能够正常编译运行了。

@OptIn 的效果域能够是办法(函数)、类、文件:

// 1. 注解办法
@OptIn(ExperimentalFoundationApi::class)
fun Test() {
}
// 2. 注解类
@OptIn(ExperimentalFoundationApi::class)
class Test {
}
// 3. 注解整个文件
@file:OptIn(ExperimentalFoundationApi::class)

别离对应这个办法挑选参加、这个类中的一切办法都挑选参加、整个文件中的一切办法,函数,类都参加。

需求留意的是,直接运用 OptIn(ExperimentalFoundationApi::class) 表明的是不传递挑选参加,即假如咱们在 Test() 函数中注解了 OptIn(ExperimentalFoundationApi::class) ,则调用 Test() 的地方不需求再声明 OptIn(xxx)

使用 Kotlin 的 Opt-in (选择加入)功能注解API提示当前非稳定API

假如咱们想要让挑选参加传递下去,则能够更改 Test() 的注解为 @ExperimentalFoundationApi,此刻调用了 Test() 的地方也需求声明挑选参加:

使用 Kotlin 的 Opt-in (选择加入)功能注解API提示当前非稳定API

最终,可能有人想问,我不想到处都写上 OptIn(xxxx) 怎样办?就算能够注解给整个文件我也觉得很麻烦啊。

那么,你能够挑选直接给整个模块(Moudle)都加上注解,咱们需求给当时模块的编译装备加上以下编译选项:

tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
    kotlinOptions {
        freeCompilerArgs += "-opt-in=org.mylibrary.OptInAnnotation"
    }
}

需求留意的是关于 Kotlin 1.6.0 之前的版别,请将 -opt-in 替换为 -Xopt-in

别的,假如运用的是 kts,则为:

tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
    kotlinOptions.freeCompilerArgs += "-opt-in=org.mylibrary.OptInAnnotation"
}

怎么自己编写

上面咱们简单解说了怎么运用 Opt-in 。咱们现在应该对 Opt-in 有了一个大致的理解,所以接下来咱们解说怎么自己写一个 Opt-in 注解。

创立挑选参加注解和创立一般注解相同,仅仅多了一个额外的装备选项:

@RequiresOptIn("直接运用该办法可能会导致意想不到的错误,请承认已知晓该办法可能会发生的问题后再运用", RequiresOptIn.Level.ERROR)
annotation class NotSafeForUse

在上面的代码中咱们创立了一个名为 NotSafeForUse 的注解。

而且为 NotSafeForUse 添加了一个 @RequiresOptIn 注解,该注解用于声明 NotSafeForUse 是一个挑选参加的注解。

@RequiresOptIn 接收两个参数:

  • message 即运用时的提示文本
  • level 正告等级

正告等级可挑选 RequiresOptIn.Level.ERRORRequiresOptIn.Level.WARNING

ERROR 表明被注解的地方强制启用挑选参加,假如不声明挑选参加,则编译将不通过:

@NotSafeForUse
fun testFun() {
}
@RequiresOptIn("直接运用该办法可能会导致意想不到的错误,请承认已知晓该办法可能会发生的问题后再运用", 
annotation class NotSafeForUse

以上代码在IDE会被标示红色下划线正告,而且编译时将报错:

使用 Kotlin 的 Opt-in (选择加入)功能注解API提示当前非稳定API

假如把等级改为 WARNING 则仅正告而不会导致编译失利,一起 IDE 也只会提示弱化的正告:

使用 Kotlin 的 Opt-in (选择加入)功能注解API提示当前非稳定API

相同的,一般注解能够运用的装备参数,挑选参加也能够运用:

@RequiresOptIn("直接运用该办法可能会导致意想不到的错误,请承认已知晓该办法可能会发生的问题后再运用", RequiresOptIn.Level.ERROR)
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
annotation class NotSafeForUse

需求留意的是,@Retention 需求为 BINARYRUNTIME

别的,关于挑选参加的注解,还需求满足以下条件:

  • No EXPRESSION, FILE, TYPE, or TYPE_PARAMETER among targets
  • No parameters.

其他问题

在 Kotlin 1.7.0 之前,Opt-in 本身也处于 Opt-in 状况,所以假如咱们的 Kotlin 版别在 1.7.0 之前,想要运用 Opt-in 必须先声明 Opt-in Opt-in(绕口令呢?)

为了声明运用挑选参加,咱们需求添加编译装备:

tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
    kotlinOptions.freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn"
}

关于 kts 则运用:

tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
    kotlinOptions.freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
}

假如不添加这个编译选项的话。直接运用 Opt-in 会正告:

使用 Kotlin 的 Opt-in (选择加入)功能注解API提示当前非稳定API

参考材料

  1. Opt-in requirements
  2. What’s new in Kotlin 1.7.0