Что нового в Kotlin 2.4.20-RC3
Вышел Kotlin 2.4.20-RC3! Вот некоторые подробности об этом выпуске EAP:
Стандартная библиотека: поддержка восстановления трассировки стека корутин и новые функции для проверки равенства и уникальности элементов коллекции
Kotlin/Native: новые возможности экспорта в Swift и автоматическая генерация файлов
Package.swiftдля зависимостей SwiftPMKotlin/JS: новый DSL для тестирования в браузере и поддержка экспорта suspend-лямбд в виде асинхронных функций
API средств сборки: поддержка новых целевых платформ: Kotlin/JS, Kotlin/Wasm и метаданных Kotlin
Компилятор Kotlin: экспериментальный выпуск нативного образа
Обновление до Kotlin 2.4.20-RC3
Последняя версия Kotlin включена в последние версии IntelliJ IDEA и Android Studio.
Чтобы обновиться до новой версии Kotlin, убедитесь, что ваша IDE обновлена до последней версии, и измените версию Kotlin на 2.4.20-RC3 в скриптах сборки.
Новые возможности
В этом выпуске доступны следующие предварительные возможности. Сюда входят возможности со статусами Beta, Alpha и Experimental:
Стандартная библиотека
В Kotlin 2.4.20-RC3 добавлена поддержка восстановления трассировки стека корутин, а также новые функции для проверки равенства и уникальности элементов коллекции.
Поддержка восстановления трассировки стека корутин
В Kotlin 2.4.20-RC3 в стандартную библиотеку добавлен интерфейс StackTraceRecoverable. Это улучшает интеграцию с библиотекой kotlinx.coroutines, поскольку позволяет определить, как создавать новые экземпляры исключений для восстановления трассировки стека, не добавляя зависимость от kotlinx.coroutines.
Восстановление трассировки стека помогает при отладке, когда одна корутина выбрасывает исключение, а другая повторно его выбрасывает. Оно позволяет увидеть, где возникло исключение и где другая корутина повторно его выбросила.
Библиотека kotlinx.coroutines выполняет восстановление трассировки стека, создавая новый экземпляр исключения с дополнительной информацией о трассировке стека корутины. Это происходит автоматически для исключений с конструкторами, принимающими только сообщение об исключении, только причину, оба аргумента или не принимающими аргументов.
Если конструктору исключения требуются дополнительные аргументы, например номер строки или код ошибки, реализуйте интерфейс StackTraceRecoverable, чтобы определить, как библиотека kotlinx.coroutines создает новый экземпляр этого исключения.
Чтобы реализовать интерфейс, переопределите функцию copyForStackTraceRecovery(). В переопределении верните новый экземпляр исключения для восстановления трассировки стека или null, если не хотите, чтобы библиотека kotlinx.coroutines копировала исключение.
Эти API имеют статус Experimental и требуют явного согласия с помощью аннотации @OptIn(ExperimentalStdlibCoroutineSupportApi::class).
Ниже приведен пример пользовательского исключения, которое сохраняет свойство line при создании нового экземпляра для восстановления трассировки стека:
import kotlin.coroutines.ExperimentalStdlibCoroutineSupportApi
import kotlin.coroutines.debug.StackTraceRecoverable
@OptIn(ExperimentalStdlibCoroutineSupportApi::class)
class FileEditException
// The implementation requires a private constructor
// to pass the cause to the IllegalStateException constructor
private constructor(
val line: Int,
private val detail: String,
cause: Throwable?,
) : IllegalStateException("When editing line $line: $detail", cause),
// Implements StackTraceRecoverable for stack trace recovery
StackTraceRecoverable<FileEditException> {
constructor(line: Int, detail: String) : this(line, detail, null)
// Copies the line number and message details
override fun copyForStackTraceRecovery(): FileEditException =
FileEditException(line, detail, this)
}
fun main() {
val original = FileEditException(15, "Unexpected token")
// Normally, you don't need to call this function directly unless you're testing its behavior
// The kotlinx.coroutines library invokes it automatically during stack trace recovery
val copy = original.copyForStackTraceRecovery()
println(copy.message)
// When editing line 15: Unexpected token
println(copy.cause == original)
// true
}
Дополнительную информацию см. в KEEP этой возможности.
Будем благодарны за ваши отзывы в YouTrack.
Новые функции для проверки равенства и уникальности элементов коллекции
До выхода Kotlin 2.4.20-RC3 для проверки того, что все элементы коллекции различны или равны, приходилось использовать неэффективные шаблоны кода.
В Kotlin 2.4.20-RC3 добавлены экспериментальные функции, решающие эту задачу:
Функция |
Проверяет |
|---|---|
|
Уникальность каждого значения в коллекции. |
|
Уникальность значения выбранного свойства у каждого объекта. |
|
Равенство всех значений в коллекции. |
|
Равенство значений выбранного свойства у всех объектов. |
Эти функции можно использовать с коллекциями, последовательностями и массивами. Они сравнивают элементы с помощью структурного равенства, как и другие операции с коллекциями.
Эти функции имеют статус Experimental и требуют явного согласия с помощью аннотации @OptIn(ExperimentalStdlibApi::class) или параметра компилятора -opt-in=kotlin.ExperimentalStdlibApi:
@OptIn(ExperimentalStdlibApi::class)
fun main() {
data class Response(
val participantId: String,
val answer: String,
val responseDate: String
)
val responses = listOf(
Response("P001", "Yes", "2026-07-21"),
Response("P002", "Maybe", "2026-07-21"),
Response("P003", "No", "2026-07-21")
)
// Checks if all participants gave the same answer
println(responses.allEqualBy { it.answer })
// false
// Checks for duplicate participants
println(responses.allDistinctBy { it.participantId })
// true
// Checks if all responses were submitted on the same date
println(responses.allEqualBy { it.responseDate })
// true
val answers = responses.map { it.answer }
// Checks if answers are identical
println(answers.allEqual())
// false
// Checks if answers are distinct
println(answers.allDistinct())
// true
}
Будем рады узнать о вашем опыте использования этих функций и получить отзывы в YouTrack.
Kotlin/Native
В Kotlin 2.4.20-RC3 добавлены новые возможности экспорта в Swift, включая поддержку sealed-классов и межъязыкового наследования, а также автоматическая генерация файлов Package.swift для зависимостей SwiftPM.
Новые возможности экспорта в Swift
Sealed-классы
В Kotlin 2.4.20-RC3 добавлена поддержка sealed-классов и интерфейсов при экспорте в Swift.
Раньше для каждого оператора switch над sealed-типом приходилось записывать вариант default. Теперь sealed-иерархии, объявленные в Kotlin, преобразуются в перечисления Swift, что позволяет использовать исчерпывающие операторы switch с полной поддержкой автодополнения в Xcode.
При экспорте в Swift для каждого sealed-типа создается метод .sealedType(). Этот метод возвращает перечисление Swift, варианты которого соответствуют непосредственным подклассам sealed-иерархии. Эти вызовы можно вкладывать, чтобы сопоставлять более глубокие уровни иерархии.
Например, объявите в Kotlin sealed-интерфейс с иерархией классов:
// Kotlin
sealed interface Shape
class Circle : Shape {
override fun toString(): String = "Circle"
}
class Rectangle : Shape {
override fun toString(): String = "Rectangle"
}
fun createCircle(): Shape = Circle()
На стороне Swift можно использовать исчерпывающий switch без варианта default:
// Swift
let shape = createCircle()
let name = switch shape.sealedType() {
case let .circle(type): "It's a \(type.value)"
case let .rectangle(type): "It's a \(type.value)"
}
// name == "It's a Circle"
Поскольку switch является исчерпывающим, компилятор предупредит вас, если в sealed-иерархию будет добавлен новый подкласс. Так вы сможете сразу обработать его, не полагаясь на вариант default.
Межъязыковое наследование при экспорте в Swift
В Kotlin 2.4.20-RC3 добавлена поддержка межъязыкового наследования при экспорте в Swift.
Распространенный сценарий использования этой возможности — шаблон обратного импорта, при котором контракт определяется в Kotlin, а платформенные реализации предоставляются на стороне Swift. Это особенно полезно, если необходимо использовать библиотеки, написанные на чистом Swift и не импортируемые напрямую в Kotlin.
Чтобы реализовать этот шаблон, объявите в Kotlin суперкласс, от которого будет наследоваться реализация на Swift, а также интерфейс Kotlin. Затем реализуйте интерфейс в Swift и передайте объект Swift функциям Kotlin, принимающим этот интерфейс. Например, для библиотеки CryptoKit:
-
На стороне Kotlin объявите базовый класс
openи интерфейс Kotlin с функцией, которая принимает его:// Kotlin interface CryptoProvider { fun hashMD5(input: String): String } fun processHash(provider: CryptoProvider, input: String): String = provider.hashMD5(input) open class SwiftBase -
На стороне Swift унаследуйте класс
SwiftBase, экспортированный из Kotlin, реализуйте интерфейс с помощью библиотеки на чистом Swift и передайте объект обратно в Kotlin:// Swift import CryptoKit final class IosCryptoProvider: SwiftBase, CryptoProvider { func hashMD5(input: String) -> String { guard let data = input.data(using: .utf8) else { return "failed" } return Insecure.MD5.hash(data: data).description } } let provider = IosCryptoProvider() // The call is dispatched to the Swift implementation print(processHash(provider: provider, input: "Hello, world!"))
Получив объект Swift, Kotlin воспринимает его как реализацию обычного интерфейса и выполняет код Swift.
Дополнительную информацию об экспорте в Swift см. в нашей документации.
Генерация Package.swift для зависимостей SwiftPM
При экспорте XCFramework с зависимостями от пакетов SwiftPM необходимо опубликовать полученный пакет SwiftPM, чтобы он разрешался правильно. Для этого задача Gradle assembleSharedXCFramework теперь создает файл Package.swift, который следует распространять вместе с XCFramework.
Подробности см. на странице экспорта SwiftPM.
Kotlin/Wasm
В Kotlin 2.4.20-RC3 изменен способ обработки Kotlin/Wasm вызовов require() верхнего уровня в объявлениях @JsFun, порядок инициализации объектов-компаньонов приведен в соответствие с поведением JVM, а в плагин Kotlin Gradle добавлена поддержка Wasmtime в качестве среды выполнения для целевой платформы wasmWasi.
Изменения в вызовах require() верхнего уровня в объявлениях @JsFun
Теперь Kotlin/Wasm сообщает об ошибке, если объявление @JsFun использует функцию require() верхнего уровня.
Раньше компилятор создавал переменную require в файле import-object.mjs, позволяя объявлениям @JsFun вызывать require().
Такое поведение непреднамеренно раскрывала деталь реализации компилятора. Чтобы упростить переход с него, Kotlin/Wasm удаляет это сгенерированное объявление require, и теперь компилятор сообщает об ошибках при таких вызовах. Например:
// Reports an error
@JsFun("(mod) => require(mod)")
external fun loadModule(mod: String): JsAny
Чтобы подготовиться к этому изменению, замените вызовы require() верхнего уровня в объявлениях @JsFun аннотацией @JsModule:
@JsModule("module")
external val module: Module
external interface Module {
// Defines the expected module members
}
Для динамической загрузки модулей используйте выражение import(). Добавьте магический комментарий /* webpackIgnore: true */, чтобы предотвратить анализ динамического импорта инструментом webpack:
@JsFun("""
((module) => () => module)(
await import(/* webpackIgnore: true */ "module")
)
""")
private external fun loadModuleDynamically(): JsAny?
Выражение import() также можно использовать условно. Например, можно загружать модуль только при запуске в Node.js:
@JsFun("""
((module) => () => module)(
((typeof process !== "undefined") && (process.release.name === "node"))
? await import(/* webpackIgnore: true */ "module")
: null
)
""")
private external fun loadNodeModule(): JsAny?
Если ваш проект зависит от библиотек, которым требуется функция require() верхнего уровня, в качестве обходного решения добавьте ее как свойство globalThis:
@JsFun("""
((module) => {
globalThis.require = module.default.createRequire(import.meta.url)
return () => {}
})(await import("node:module"))
""")
external fun defineRequire()
Если у вас возникнут проблемы, поделитесь отзывом в нашем трекере задач.
Улучшенный порядок инициализации объектов-компаньонов
Теперь Kotlin/Wasm инициализирует объекты-компаньоны суперклассов перед объектами-компаньонами подклассов, как и JVM. Раньше порядок инициализации мог быть обратным, что приводило к несогласованному поведению на разных платформах.
Это обновление повышает согласованность между платформами и уменьшает платформенные различия в поведении при инициализации классов. Кроме того, оно обеспечивает правильную обработку инициализации объектов-компаньонов в глубоких иерархиях наследования, в том числе в случаях, когда промежуточные классы не объявляют объекты-компаньоны.
Поддержка Wasmtime в плагине Kotlin Gradle
В Kotlin 2.4.20-RC3 добавлена поддержка Wasmtime в качестве среды выполнения для целевой платформы wasmWasi в плагине Kotlin Gradle.
Раньше для целевой платформы wasmWasi поддерживалась только среда выполнения Node.js, для которой требовалась загрузка JavaScript-кода для запуска приложений WASI. Благодаря поддержке Wasmtime теперь можно запускать приложения Kotlin/Wasm в автономной среде выполнения WebAssembly.
Чтобы использовать Wasmtime в качестве среды выполнения для целевой платформы wasmWasi, добавьте wasmtime() в файл сборки Gradle:
kotlin {
wasmWasi {
wasmtime()
}
}
Будем благодарны за ваши отзывы в YouTrack.
Kotlin/JS
В Kotlin 2.4.20-RC3 добавлен новый экспериментальный DSL для тестирования в браузере и поддержка экспорта suspend-лямбд в виде асинхронных функций JavaScript.
Новый DSL для тестирования в браузере
В Kotlin 2.4.20-RC3 добавлен новый экспериментальный DSL для запуска тестов Kotlin/JS в браузере.
Сейчас плагин Kotlin Gradle использует Karma в качестве средства запуска браузеров для выполнения тестов JavaScript в разных браузерах. Проект Karma уже два года считается устаревшим, поэтому мы начали изучать альтернативные способы поддержки тестирования в браузере.
Новый DSL призван заменить Karma в качестве инструмента, управляющего различными средствами, и включает:
Mocha в качестве средства запуска тестов.
Webpack в качестве сборщика (в будущих выпусках он будет заменен на Vite).
Playwright в качестве драйвера браузера и средства управления дистрибутивами, поддерживающего браузерные движки Chromium, Firefox и WebKit (Safari).
Чтобы попробовать новый DSL для тестирования, добавьте блок явного согласия test{} внутрь browser{} для целевой платформы Kotlin/JS:
import org.jetbrains.kotlin.gradle.ExperimentalJsTestDsl
import kotlin.time.Duration.Companion.seconds
kotlin {
js {
browser {
@OptIn(ExperimentalJsTestDsl::class)
// Add and configure the new test{} block
test {
// Configure default timeout for all runners
timeout = 2.seconds
// Configure headless mode using Gradle providers
headless = providers
.environmentVariable("IS_IN_CI")
.map { it.toBoolean() }
.orElse(false)
// Enable and configure Chromium test runner
chromium {
// Override the common timeout option
timeout = 5.seconds
// Add extra launch arguments
launchArgs.add("--no-sandbox")
}
// Enable Firefox test runner
firefox()
// Enable WebKit test runner
webkit()
// Enable and configure an additional WebKit test runner
webkit("noheadless") {
// Set up custom options
headless = false
}
}
}
}
}
Новый DSL активно разрабатывается. Будем благодарны за ваши отзывы в YouTrack.
Поддержка экспорта suspend-лямбд в виде асинхронных функций
В Kotlin 2.4.20-RC3 можно экспортировать лямбда-выражения с модификатором suspend в виде функций JavaScript async.
Раньше из библиотек Kotlin/JS нельзя было экспортировать объявления, содержащие suspend-лямбды. Теперь компилятор Kotlin автоматически выполняет сопряжение между функциями Kotlin suspend и встроенной моделью JavaScript async/await, что полезно для проектов со смешанным кодом Kotlin и TypeScript.
Чтобы включить эту возможность, добавьте следующий параметр компилятора в файл build.gradle.kts:
kotlin {
js {
compilations.all {
compileTaskProvider.configure {
compilerOptions {
freeCompilerArgs.add("-Xsuspend-lambda-exporting")
}
}
}
}
}
Затем пометьте соответствующие объявления аннотацией @JsExport:
// Kotlin
@JsExport
class TaskRunner {
suspend fun runTask(task: suspend () -> String): String {
return task()
}
}
Со стороны TypeScript suspend-лямбда выглядит как обычная функция async:
// TypeScript
import { TaskRunner } from "..."
const runner = new TaskRunner();
const result = await runner.runTask(async () => "done");
console.log(result); // "done"
Дополнительную информацию об аннотации @JsExport см. в нашей документации.
API средств сборки
Поддержка Kotlin/JS, Kotlin/Wasm и метаданных Kotlin
В Kotlin 2.2.0 API средств сборки (BTA) стал доступен для Kotlin/JVM. В Kotlin 2.4.20-RC3 сделан следующий шаг к стабилизации BTA — добавлена поддержка новых целевых платформ: Kotlin/JS, Kotlin/Wasm и метаданных Kotlin.
Это позволяет плагину Kotlin Gradle более последовательно взаимодействовать с компилятором. В некоторых случаях также можно добиться более быстрой и стабильной компиляции.
BTA — это универсальный API, выступающий в качестве уровня абстракции между системами сборки и экосистемой компилятора Kotlin. Он упрощает поддержку возможностей Kotlin и совместимость с компилятором Kotlin в доступных средствах сборки.
В Kotlin 2.4.20-RC3 BTA доступен для новых целевых платформ в виде возможности, требующей явного включения. Чтобы попробовать его, добавьте соответствующие свойства в файл gradle.properties:
kotlin.wasm.runViaBuildToolsApi=true kotlin.js.runViaBuildToolsApi=true kotlin.metadata.runViaBuildToolsApi=true
Начиная с Kotlin 2.5.0 мы планируем включить BTA по умолчанию для Kotlin/JS, Kotlin/Wasm и метаданных Kotlin.
Если вас интересует предложение BTA или вы хотите поделиться отзывом, ознакомьтесь с этим KEEP.
Компилятор Kotlin: нативный образ
В Kotlin 2.4.20-RC3 представлен первый выпуск нативного образа компилятора Kotlin со статусом Experimental. Нативный образ служит полноценной заменой стандартному инструменту командной строки kotlinc, обеспечивая при этом более быстрый запуск и высокую производительность.
Чтобы попробовать нативный образ, скачайте сборку на странице выпусков GitHub.
В нативный образ также включены следующие плагины компилятора, которые можно использовать с параметрами CLI -Xplugin или -Xcompiler-plugin:
Дополнительную информацию о нативном образе компилятора Kotlin см. в файле README.
© 2010–2026 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/whatsnew-eap.html