Kotlin:2.1.20 的新特性

发布于:2025-07-06 ⋅ 阅读:(19) ⋅ 点赞:(0)

一、概述

The Kotlin 2.1.20 release is here! Here are the main highlights:
Kotlin 2.1.20发布了,主要亮点如下:

  • K2 compiler updates: updates to the new kapt and Lombok plugins
  • Kotlin Multiplatform: new DSL to replace Gradle’s Application plugin
  • Kotlin/Native: support for Xcode 16.3 and a new inlining optimization
  • Kotlin/Wasm: default custom formatters, support for DWARF, and migration to Provider API
  • Gradle support: compatibility with Gradle’s Isolated Projects and custom publication variants
  • Standard library: common atomic types, improved UUID support, and new time-tracking functionality
  • Compose compiler: relaxed restrictions on @Composable functions and other updates
  • Documentation: notable improvements to the Kotlin documentation.

二、常见原子类型

在Kotlin 2.1.20中,我们在标准库的Kotlin .concurrent.atomics包中引入了公共原子类型,为线程安全操作启用了共享的、独立于平台的代码。
通过消除跨源集复制原子相关逻辑的需要,这简化了Kotlin Multiplatform项目的开发。

kotlin.concurrent.atomics包及其属性是实验性的。要选择加入,使用@OptIn(ExperimentalAtomicApi::class)注释或编译器选项-opt-in=kotlin.ExperimentalAtomicApi。

下面是一个示例,展示了如何使用AtomicInt在多个线程中安全地计数已处理的项:

示例执行效果图

在这里插入图片描述

@OptIn(ExperimentalAtomicApi::class)
suspend fun testExperimentalAtomicApi() {
    var processedItems = AtomicInt(0)
    val totalItems = 100
    val items = List(totalItems) { "item$it" }
    // Splits the items into chunks for processing by multiple coroutines
    val chunkSize = 20
    val itemChunks = items.chunked(chunkSize)// chunked函数 将List分割为指定大小的子数组(List),
    // itemChunks 5个数组,每个数组里有20个元素
    coroutineScope {
        for (chunk in itemChunks) {
            launch {
                for (item in chunk) {
                    println("Processing $item in thread ${Thread.currentThread()}")
                    processedItems.incrementAndFetch()
                }
            }
        }
    }
    println("processedItems = $processedItems")
}

suspend fun main() {
    testExperimentalAtomicApi()
}

为了在Kotlin的原子类型和Java的Java .util.concurrent.atomic原子类型之间实现无缝的互操作性,API提供了. asjavaatomic()和. askotlinatomic()扩展函数。
在JVM上,Kotlin原子和Java原子在运行时是相同的类型,因此您可以在没有任何开销的情况下将Java原子转换为Kotlin原子,反之亦然。

下面的例子展示了Kotlin和Java原子类型是如何协同工作的:

执行示例效果图

在这里插入图片描述


@OptIn(ExperimentalAtomicApi::class)
fun testExperimentalAtomicApi11(){
    // Converts Kotlin AtomicInt to Java's AtomicInteger
    val kotlinAtomic = AtomicInt(24)
    val javaAtomic: AtomicInteger = kotlinAtomic.asJavaAtomic()
    println("转换 Java atomic 原始 value: ${javaAtomic.get()}")
    // 转换 Java atomic 原始 value: 24
    javaAtomic.incrementAndGet()
    println("转换 Java atomic 后 自增1次 value: ${javaAtomic.get()}")
    // 转换 Java atomic 后 自增1次 value: 25

    // Converts Java's AtomicInteger back to Kotlin's AtomicInt
    val kotlinAgain: AtomicInt = javaAtomic.asKotlinAtomic()
    println("转换 Kotlin atomic 原始 value: ${kotlinAgain.load()}")
    //转换 Kotlin atomic 原始 value: 25
    kotlinAgain.incrementAndFetch()
    println("转换 Kotlin atomic 后 自增1次 value: ${kotlinAgain.load()}")
    // 转换 Kotlin atomic 后 自增1次 value: 26

}

suspend fun main() {
    testExperimentalAtomicApi11()
}

三、UUID解析、格式和可比性的变化

JetBrains团队继续在2.0.20中改进对标准库中引入的uuid的支持。

以前,parse()函数只接受十六进制和破折号格式的uuid。在Kotlin 2.1.20中,可以对十六进制-破折号和普通十六进制(不带破折号)格式使用parse()。

在这个版本中,我们还引入了一些特定于十六进制和破折号格式操作的函数:

  • parseHexDash()从十六进制和破折号格式解析uid。
  • toHexDashString()将Uuid转换为十六进制和破折号格式的String(镜像toString()的功能)。

这些函数的工作方式类似于前面介绍的十六进制格式的parseHex()和toHexString()。解析和格式化功能的显式命名应该会提高代码的清晰度和您使用uid的总体体验。

Kotlin中的uid现在是可比的。从Kotlin 2.1.20开始,您可以直接比较和排序Uuid类型的值。这允许使用<和>操作符和标准库扩展,这些扩展专门用于Comparable类型或它们的集合(例如sorted()),并且它还允许将uuid传递给需要Comparable接口的任何函数或api。

请记住,标准库中的UUID支持仍然是实验性的。要选择加入,使用@OptIn(ExperimentalUuidApi::class)注释或编译器选项-opt-in=kotlin.uuid.ExperimentalUuidApi::

执行示例效果图

在这里插入图片描述

@OptIn(ExperimentalUuidApi::class)
fun testExperimentalUuidApi(){
    // parse() accepts a UUID in a plain hexadecimal format
    val uuid = Uuid.parse("550e8400e29b41d4a716446655440000")

    // Converts it to the hex-and-dash format
    val hexDashFormat = uuid.toHexDashString()

    // Outputs the UUID in the hex-and-dash format
    println(hexDashFormat)
    // 550e8400-e29b-41d4-a716-446655440000

    // Outputs UUIDs in ascending order
    println(
        listOf(
            uuid,
            Uuid.parse("780e8400e29b41d4a716446655440005"),
            Uuid.parse("5ab88400e29b41d4a716446655440076")
        ).sorted()
    )
    // [550e8400-e29b-41d4-a716-446655440000, 5ab88400-e29b-41d4-a716-446655440076, 780e8400-e29b-41d4-a716-446655440005]
}

fun main() {
    testExperimentalUuidApi()
}

四、新的时间跟踪功能

从Kotlin 2.1.20开始,标准库提供了表示某个时刻的能力。此功能以前仅在Kotlin官方库kotlinx-datetime中可用。

kotlinx.datetime.Clock接口以kotlin.time.Clock的形式引入标准库,kotlinx.datetime.Instant类以kotlin.time.Instant的形式引入标准库。这些概念很自然地与标准库中的时间包保持一致,因为与kotlinx-datetime中保留的更复杂的日历和时区功能相比,它们只关注时间中的时刻。

当您需要精确的时间跟踪而不考虑时区或日期时,Instant和Clock是有用的。例如,您可以使用它们来记录带有时间戳的事件,度量两个时间点之间的持续时间,并获得系统进程的当前时刻。

为了提供与其他语言的互操作性,可以使用额外的转换器功能:

  • tokotlininstant()将时间值转换为kotlin.time.Instant实例。
  • tojavainstant()将kotlin.time.Instant值转换为java.time.Instant值。
  • Instant.toJSDate()将kotlin.time.Instant值转换为JSDate类的一个实例。这种转换并不精确;JS使用毫秒级的精度来表示日期,而Kotlin允许纳秒级的分辨率。

标准库的新时间特性仍处于试验阶段。要选择加入,请使用@OptIn(ExperimentalTime::class)注释:

执行示例效果图

在这里插入图片描述

@OptIn(ExperimentalTime::class)
fun testTimeClock() {
    // Get the current moment in time
    val currentInstant = Clock.System.now()
    println("Current time: $currentInstant")

    // Find the difference between two moments in time
    val pastInstant = Instant.parse("2023-01-01T00:00:00Z")
    val duration = currentInstant - pastInstant

    println("Time elapsed since 2023-01-01: $duration")
}
fun main() {
    testTimeClock()
}