Kotlin 2.1.0-Beta1 看看又有哪些新东西
Kotlin 2.1.0-Beta1版本已发布!本文档包含有关此EAP版本的一些详细信息。
IDEA 支持
支持2.1.0-Beta1的Kotlin插件捆绑在最新的IntelliJ IDEA和Android Studio中。您不需要更新IDE中的Kotlin插件。您只需在构建脚本中将Kotlin版本更改为2.1.0-Beta1。
支持要求选择加入以扩展API
Kotlin 2.1.0-Beta1引入了@SubclassOptInRequired
注释。此注释允许库作者在用户实现实验接口或扩展实验类之前要求明确选择加入。
当库API足够稳定使用,但可能会随着新的抽象函数而演变,使其继承不稳定时,此功能非常有用。
要将选择加入要求添加到API元素中,请使用@SubclassOptInRequired
注释并引用注释类:
@RequiresOptIn(
level = RequiresOptIn.Level.WARNING,
message = "Interfaces in this library are experimental"
)
annotation class UnstableApi()
@SubclassOptInRequired(UnstableApi::class)
interface CoreLibraryApi
在本例中,CoreLibraryApi
接口要求用户在实现之前选择加入。用户可以像这样选择加入:
@OptIn(UnstableApi::class)
interface MyImplementation : CoreLibraryApi
当您使用
@SubclassOptInRequired
注释要求选择加入时,该要求不会传播到任何内部或嵌套类。
有关如何在API中使用@SubclassOptInRequired
注释的真实示例,请查看kotlinx.coroutines
库中的SharedFlow
接口。
新语言功能的预览
在带有K2编译器的Kotlin 2.0.0发布后,JetBrains的Kotlin团队正专注于通过新功能改进语言。在这个版本中,我们很高兴地宣布几个新的语言设计功能。
1. Guard conditions in when
with a subject
描述
该提案是在带有“主题”的表达式中引入分支扩展,以解锁在分支条件下执行多次检查的能力
sealed interface Animal {
class Cat(val mouseHunter: Boolean) : Animal
data object Dog : Animal
}
fun feedAnimal(animal: Animal) {
when (animal) {
is Animal.Cat if !animal.mouseHunter -> feedCat() // note the part with "if"
Animal.Dog -> feedDog()
}
}
如何启用实验功能
Kotlin 2.1是必需的
- In CLI:
kotlinc -Xwhen-guards main.kt
- In Gradle:
kotlin.compilerOptions.freeCompilerArgs.add("-Xwhen-guards")
发布计划
- 在 2.1 中预览
- 何时稳定?(尚未决定)
原始请求
在某些when表达式的情况下,必须检查其他条件才能提供结果。
一般来说,这会导致具有提取函数和早期回报的代码,这将原始的漂亮抽象变成了更难阅读的代码。
如果我们在“when”中添加保护条件,可以缓解一些情况,例如(strawman语法类似于为类型参数提供额外的约束):
fun test(x: Being) =
when (x) {
is Animal where x is Fluffy ->
"It's so cute!"
// other branches
}
请注意,上述示例不能使用“when”和“if”表达式的组合来表达。
您可以将“when-with-subject”转换为“when-without-suject”,但它也会影响其他分支。
此内容查看至
Guard conditions in when with a subject
2. Non-local break
and continue
描述
在Kotlin中,可以写 non-local returns。Non-local return “jumps” 在内联lambda上。但禁止使用相同的功能,但用于 continue
和 break
。
这个问题建议在与当前返回语句相同的情况下使用 non-local continue
和 break
成为可能。
fun main() {
for (i in 1..10) {
run {
when {
i == 0 -> continue // it's currently prohibited. The suggestion is to allow such statements.
i == 1 -> break // it's currently prohibited. The suggestion is to allow such statements.
else -> println(i)
}
}
}
}
如何启用实验功能
Kotlin 2.1是必需的
- In CLI:
kotlinc -Xnon-local-break-continue main.kt
- In Gradle:
kotlin.compilerOptions.freeCompilerArgs.add("-Xwhen-guards")
发布计划
- 在 2.1 中预览
- 在 2.2 中稳定
时间线
- 保留并对该功能进行了广泛检查。不幸的是,发现了CFA/DFA问题(KT-54906),在K1中修复它们可能非常危险。因此,决定将该功能推迟到K2
- 2024年1月11日。不幸的是,在K2中,该功能仅在JVM中有效。(KT-56466)该功能推迟到2.2(预览推迟到2.1)
此内容查看至
3. Multidollar interpolation: improved handling of $
in string literals.
描述
在Kotlin中,多行字符串部分是原始的:没有像单行字符串那样有\n
、\t
等转义序列。然而,$
用于标记插值,这使得在标识符前面使用$符号变得困难:
val jsonSchema: String = """
{
"$id": "https://example.com/product.schema.json",
}"""
在这里,编译器发出一个错误,提示id未解决,如果编写"\$id"
,也会发生同样的情况,因为多行字符串中没有转义序列。实际的解决方法是使用一个相当棘手的序列${'$'}
在标识符前面放置一个美元符号:
"""
"${'$'}id": "https://example.com/product.schema.json",
"""
提议的解决方案
拟议解决方案背后的主要思想是,插值必须以与字符串字面值开头的$
字符数量相同:
val name = "Kotlin"
"I'm $name" // I'm Kotlin
$"I'm $name" // I'm Kotlin
$$"I'm $name" // I'm $name
$$"I'm $$name" // I'm Kotlin
$$"""
// interpolation
for (c in $$name) {
println(c)
}
// no interpolation
println("$note or €note?")
"""
如何启用实验功能
Kotlin 2.1是必需的
- In CLI:
kotlinc -Xmulti-dollar-interpolation main.kt
- In Gradle:
kotlin.compilerOptions.freeCompilerArgs.add("-Xmulti-dollar-interpolation")
发布计划
- 在 2.1 中预览
- 在 2.2 中正式 (计划,不保证)
原始问题和讨论
让我从一个小nit开始——Kotlin没有“原始字符串”的概念。Kotlin有“多行字符串”。所有Kotlin字符串都支持使用$
字符进行字符串插值。它可以在单行字符串中作为\$
转义,但目前不能在多行字符串中转义。那确实是个问题。
这里有两种可能的通用解决方案路径,它们并不相互排斥(它们都涵盖了一些不同的用例):
$
转义:提供在多行字符串中转义$
的方法。最好和最自然的方法是通过与单行字符串相同的$序列(无需学习新的语法),但这是一个向后不兼容的变化,需要几年时间才能逐渐引入。有一些向后兼容的选项(例如${}
),但它们没有那么好,因为它们必须单独学习。无论如何,这将解决“转义问题”,但它不会解决在多行字符串中转义美元的需求,对于一些人们将重美元脚本嵌入到Kotlin源代码中的用例来说,这可能是一个亮点。- 原始字符串:正如一些人建议的那样,在Kotlin中添加一个“原始字符串”的新概念。它们是许多来自其他语言的句法灵感,在这里使用,也有一些有效的用例。然而,这意味着在语言中添加一个全新的(而且总是相当巴洛克式)语法,这总体上是一个重量级的补充,必须是一个仔细衡量的决定。
此内容查看至
Multidollar interpolation: improved handling of $ in string literals.
改进的 K2 kapt 实现
K2编译器(K2 kapt)的kapt插件在Alpha中。它可能随时被更改。
目前,使用kapt插件的项目默认使用K1编译器,支持高达1.9的Kotlin版本。
在Kotlin 1.9.20中,我们使用K2编译器(K2 kapt)启动了kapt插件的实验性实现。现在,我们改进了K2 kapt的内部实现,以减轻技术和性能问题。
虽然新的K2 kapt实现没有引入新功能,但与之前的K2 kapt实现相比,其性能有了显著改善。此外,K2 kapt插件的行为现在更接近K1 kapt。
要使用新的K2 kapt插件实现,请以与之前的K2 kapt插件相同的方式启用它。将以下标志添加到您项目的gradle.properties
文件中:
kapt.use.k2=true
在即将发布的版本中,K2 kapt实现将默认启用,而不是K1 kapt,因此您不再需要手动启用它。
使用K2 kapt插件时,您可能会在kaptGenerateStubs*
任务期间遇到编译错误,即使Gradle日志中缺少实际错误详细信息。这是一个已知的问题,当在模块中启用kapt,但没有注释处理器时会出现。解决方法是禁用模块中的kapt插件。
将Kotlin/Native的LLVM版本更新到16.0.0
在Kotlin 2.1.0-Beta1中,我们将LLVM从11.1.0版本更新到16.0.0。在某些情况下,新版本提供编译器优化和更快的编译。它还包括LLVM错误修复和安全更新。
此更新不应影响您的代码,但如果您遇到任何问题,请在Jetbrains的跟踪器中创建问题。
Gradle改进
Kotlin 2.1.0-Beta1与Gradle 7.6.3至Gradle 8.9完全兼容。
您也可以使用Gradle版本,直到最新的Gradle版本,但如果您这样做,请记住,您可能会遇到弃用警告,或者一些新的Gradle功能可能无法使用。
将最低支持的AGP版本提高到7.3.1
从Kotlin 2.1.0-Beta1开始,最低支持的Android Gradle插件版本为7.3.1。
将最低支持的Gradle版本提升到7.6.3
从Kotlin 2.1.0-Beta1开始,最低支持的Gradle版本为7.6.3。
Compose 编译器更新
支持多个稳定配置文件
Compose编译器能够解释多个稳定性配置文件,但Compose Compiler Gradle
插件的stabilityConfigurationFile
选项只允许指定单个文件。
在Kotlin 2.1.0-Beta1中,此功能进行了重新处理,允许您为单个模块使用多个稳定性配置文件:
stabilityConfigurationFile
选项已弃用。- 有一个新选项,类型为
ListProperty<RegularFile>
的stabilityConfigurationFiles
。
以下是如何使用新选项将多个文件传递给Compose编译器:
composeCompiler {
stabilityConfigurationFiles.addAll(
project.layout.projectDirectory.file("configuration-file1.conf"),
project.layout.projectDirectory.file("configuration-file2.conf"),
)
}
如何更新到Kotlin 2.1.0-Beta1
从IntelliJ IDEA 2023.3和Android Studio Iguana(2023.2.1)Canary 15开始,Kotlin插件作为IDE中包含的捆绑插件进行分发。这意味着您不能再从JetBrains Marketplace安装插件了。捆绑插件支持即将发布的Kotlin EAP版本。
要更新到新的Kotlin EAP版本,请在构建脚本中将Kotlin版本更改为2.1.0-Beta1。
我的语法建议
在有些场景,需要进行延迟初始化,而不能使用lazy
关键字,因为需要传参数进去,而现在的做法是
class Test {
private var demo: Demo? = null
/**
* 此方法会被多次执行
*/
fun test() {
if (demo == null) {
this.demo = Demo(/*...*/)
}
}
}
可以出一个空赋值运算符 ?=
class Test {
private var demo: Demo? = null
/**
* 此方法会被多次执行
*/
fun test() {
this.demo ?= Demo(/*...*/) // 当demo为空的时候初始化,这样不会影响现有代码,并且能够简化现有代码
}
}