Что нового в Kotlin 1.9.0
Вышел Kotlin 1.9.0, а компилятор K2 для JVM теперь находится в бета-версии. Кроме того, вот некоторые из основных нововведений:
Новая общая функция для получения группы захвата регулярного выражения по имени
Предварительная версия кэша конфигурации Gradle в Kotlin Multiplatform
Изменения поддержки целевой платформы Android в Kotlin Multiplatform
Предварительная версия пользовательского аллокатора памяти в Kotlin/Native
Краткий обзор обновлений также можно посмотреть в этом видео:
Поддержка IDE
Плагины Kotlin с поддержкой версии 1.9.0 доступны для:
IDE |
Поддерживаемые версии |
|---|---|
IntelliJ IDEA |
2022.3.x, 2023.1.x |
Android Studio |
Giraffe (223), Hedgehog (231)* |
*Плагин Kotlin 1.9.0 будет включен в будущие выпуски Android Studio Giraffe (223) и Hedgehog (231).
Плагин Kotlin 1.9.0 будет включен в будущие выпуски IntelliJ IDEA 2023.2.
Новые обновления компилятора Kotlin K2
Команда Kotlin в JetBrains продолжает стабилизировать компилятор K2, и в выпуске 1.9.0 появились дальнейшие улучшения. Компилятор K2 для JVM теперь находится в бета-версии.
Теперь также доступна базовая поддержка Kotlin/Native и многоплатформенных проектов.
Совместимость плагина компилятора kapt с компилятором K2
В проекте можно использовать плагин kapt вместе с компилятором K2, но с некоторыми ограничениями. Несмотря на то что languageVersion имеет значение 2.0, плагин компилятора kapt по-прежнему использует старый компилятор.
Если запустить плагин компилятора kapt в проекте, где languageVersion имеет значение 2.0, kapt автоматически переключится на 1.9 и отключит определенные проверки совместимости версий. Такое поведение эквивалентно добавлению следующих аргументов команды:
-Xskip-metadata-version-check-Xskip-prerelease-check-Xallow-unstable-dependencies
Эти проверки отключаются только для задач kapt. Для всех остальных задач компиляции продолжит использоваться новый компилятор K2.
Если при использовании kapt с компилятором K2 возникнут проблемы, сообщите о них в наш трекер проблем.
Попробуйте компилятор K2 в своем проекте
Начиная с версии 1.9.0 и до выхода Kotlin 2.0, компилятор K2 можно легко протестировать, добавив свойство Gradle kotlin.experimental.tryK2=true в файл gradle.properties. Также можно выполнить следующую команду:
./gradlew assemble -Pkotlin.experimental.tryK2=true
Это свойство Gradle автоматически устанавливает версию языка 2.0 и обновляет отчет о сборке, добавляя сведения о количестве задач Kotlin, скомпилированных с помощью компилятора K2, по сравнению с текущим компилятором:
##### 'kotlin.experimental.tryK2' results (Kotlin/Native not checked) ##### :lib:compileKotlin: 2.0 language version :app:compileKotlin: 2.0 language version ##### 100% (2/2) tasks have been compiled with Kotlin 2.0 #####
Отчеты о сборке Gradle
Отчеты о сборке Gradle теперь показывают, какой компилятор использовался для компиляции кода — текущий или K2. В Kotlin 1.9.0 эту информацию можно найти в отчетах Gradle Build Scan:


Версию Kotlin, используемую в проекте, также можно найти непосредственно в отчете о сборке:
Task info: Kotlin language version: 1.9
Текущие ограничения компилятора K2
Включение K2 в проекте Gradle связано с определенными ограничениями, которые могут затронуть проекты с версиями Gradle ниже 8.3 в следующих случаях:
Компиляция исходного кода из
buildSrc.Компиляция плагинов Gradle во включенных сборках.
Компиляция других плагинов Gradle, если они используются в проектах с версиями Gradle ниже 8.3.
Сборка зависимостей плагинов Gradle.
Если возникнут какие-либо из перечисленных выше проблем, для их решения можно выполнить следующие действия:
Установите версию языка для
buildSrc, любых плагинов Gradle и их зависимостей:
kotlin {
compilerOptions {
languageVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9)
apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9)
}
}
Обновите версию Gradle в проекте до 8.3, когда она станет доступна.
Оставьте отзыв о новом компиляторе K2
Будем рады любым вашим отзывам!
Оставьте отзыв непосредственно разработчикам K2 в Slack Kotlin: получите приглашение и присоединитесь к каналу #k2-early-adopters.
Сообщите о проблемах, с которыми вы столкнулись при работе с новым компилятором K2, в наш трекер проблем.
Включите параметр Отправлять статистику использования, чтобы разрешить JetBrains собирать анонимные данные об использовании K2.
Язык
В Kotlin 1.9.0 мы стабилизируем некоторые новые языковые возможности, представленные ранее:
Стабильная замена функции values класса enum
В версии 1.8.20 свойство entries для классов enum было представлено как экспериментальная возможность. Свойство entries — это современная и производительная замена синтетической функции values(). В версии 1.9.0 свойство entries стало стабильным.
enum class Color(val colorName: String, val rgb: String) {
RED("Red", "#FF0000"),
ORANGE("Orange", "#FF7F00"),
YELLOW("Yellow", "#FFFF00")
}
fun findByRgb(rgb: String): Color? = Color.entries.find { it.rgb == rgb }
Дополнительную информацию о свойстве entries для классов enum см. в разделе Что нового в Kotlin 1.8.20.
Стабильные data object для симметрии с классами данных
Объявления data object, представленные в Kotlin 1.8.20, теперь стабильны. Это включает функции, добавленные для симметрии с классами данных: toString(), equals() и hashCode().
Эта возможность особенно полезна в иерархиях sealed (например, в иерархии sealed class или sealed interface), поскольку объявления data object удобно использовать наряду с объявлениями data class. В этом примере объявление EndOfFile в качестве data object вместо обычного object означает, что оно автоматически получает функцию toString() без необходимости переопределять ее вручную. Это обеспечивает симметрию с соответствующими объявлениями классов данных.
sealed interface ReadResult
data class Number(val number: Int) : ReadResult
data class Text(val text: String) : ReadResult
data object EndOfFile : ReadResult
fun main() {
println(Number(7)) // Number(number=7)
println(EndOfFile) // EndOfFile
}
Дополнительную информацию см. в разделе Что нового в Kotlin 1.8.20.
Поддержка вторичных конструкторов с телами в inline-классах-значениях
Начиная с Kotlin 1.9.0, использование вторичных конструкторов с телами в inline-классах-значениях доступно по умолчанию:
@JvmInline
value class Person(private val fullName: String) {
// Allowed since Kotlin 1.4.30:
init {
check(fullName.isNotBlank()) {
"Full name shouldn't be empty"
}
}
// Allowed by default since Kotlin 1.9.0:
constructor(name: String, lastName: String) : this("$name $lastName") {
check(lastName.isNotBlank()) {
"Last name shouldn't be empty"
}
}
}
Ранее Kotlin разрешал в inline-классах только открытые первичные конструкторы. В результате было невозможно инкапсулировать базовые значения или создать inline-класс, представляющий некоторые значения с ограничениями.
По мере развития Kotlin эти проблемы были решены. В Kotlin 1.4.30 были сняты ограничения на блоки init, а в Kotlin 1.8.20 появилась предварительная версия вторичных конструкторов с телами. Теперь они доступны по умолчанию. Подробнее о развитии inline-классов Kotlin читайте в этом KEEP.
Kotlin/JVM
Начиная с версии 1.9.0, компилятор может создавать классы с версией байт-кода, соответствующей JVM 20. Кроме того, продолжается устаревание аннотации JvmDefault и устаревших режимов -Xjvm-default.
Устаревание аннотации JvmDefault и устаревших режимов -Xjvm-default
Начиная с Kotlin 1.5, использование аннотации JvmDefault было объявлено устаревшим в пользу новых режимов -Xjvm-default: all и all-compatibility. С появлением JvmDefaultWithoutCompatibility в Kotlin 1.4 и JvmDefaultWithCompatibility в Kotlin 1.6 эти режимы обеспечивают полный контроль над генерацией классов DefaultImpls, гарантируя совместимость со старым кодом Kotlin.
Поэтому в Kotlin 1.9.0 аннотация JvmDefault больше не имеет значения и помечена как устаревшая с ошибкой. В дальнейшем она будет удалена из Kotlin.
Kotlin/Native
Помимо других улучшений, этот выпуск содержит дальнейшие усовершенствования менеджера памяти Kotlin/Native, которые должны повысить его надежность и производительность:
Хук освобождения объектов Objective-C или Swift в главном потоке
Отсутствие инициализации объектов при доступе к константным значениям в Kotlin/Native
Возможность настройки автономного режима для тестов в симуляторе iOS
Предварительная версия пользовательского аллокатора памяти
В Kotlin 1.9.0 представлена предварительная версия пользовательского аллокатора памяти. Его система выделения памяти повышает производительность во время выполнения менеджера памяти Kotlin/Native.
Текущая система выделения памяти для объектов в Kotlin/Native использует универсальный аллокатор, не обладающий функциональностью для эффективной сборки мусора. Для компенсации этого ограничения он поддерживает локальные для потока связанные списки всех выделенных объектов, прежде чем сборщик мусора (GC) объединит их в один список, который можно обходить во время очистки. Такой подход имеет ряд недостатков с точки зрения производительности:
Порядок очистки не обеспечивает локальность памяти и часто приводит к разрозненному доступу к памяти, что может вызвать проблемы с производительностью.
Для каждого объекта связанные списки требуют дополнительной памяти, увеличивая ее использование, особенно при работе с большим количеством небольших объектов.
Единый список выделенных объектов затрудняет распараллеливание очистки, что может привести к проблемам с использованием памяти, если потоки-мутаторы выделяют объекты быстрее, чем поток GC успевает их собирать.
Для решения этих проблем в Kotlin 1.9.0 представлена предварительная версия пользовательского аллокатора. Он делит системную память на страницы, что позволяет выполнять независимую очистку в последовательном порядке. Каждое выделение памяти становится блоком памяти внутри страницы, а страница отслеживает размеры блоков. Разные типы страниц оптимизированы для различных размеров выделений. Последовательное расположение блоков памяти обеспечивает эффективный обход всех выделенных блоков.
При выделении памяти поток ищет подходящую страницу в зависимости от размера выделения. Потоки поддерживают набор страниц для различных категорий размеров. Обычно текущая страница для определенного размера может вместить выделение. В противном случае поток запрашивает другую страницу из общего пространства выделения памяти. Эта страница может быть уже доступна, требовать очистки или нуждаться в предварительном создании.
Новый аллокатор позволяет одновременно использовать несколько независимых пространств выделения памяти. Это даст команде Kotlin возможность экспериментировать с различными компоновками страниц для дальнейшего повышения производительности.
Дополнительную информацию о проектировании нового аллокатора см. в этом файле README.
Как включить
Добавьте параметр компилятора -Xallocator=custom:
kotlin {
macosX64("native") {
binaries.executable()
compilations.configureEach {
compilerOptions.configure {
freeCompilerArgs.add("-Xallocator=custom")
}
}
}
}
Оставить отзыв
Будем признательны за ваши отзывы в YouTrack, которые помогут улучшить пользовательский аллокатор.
Хук освобождения объектов Objective-C или Swift в главном потоке
Начиная с Kotlin 1.9.0, хук освобождения объектов Objective-C или Swift вызывается в главном потоке, если объект передается в Kotlin именно там. Прежний способ обработки ссылок на объекты Objective-C менеджером памяти Kotlin/Native мог приводить к утечкам памяти. Мы считаем, что новое поведение повысит надежность менеджера памяти.
Рассмотрим объект Objective-C, на который ссылается код Kotlin, например, когда он передается в качестве аргумента, возвращается функцией или извлекается из коллекции. В этом случае Kotlin создает собственный объект, содержащий ссылку на объект Objective-C. Когда объект Kotlin освобождается, среда выполнения Kotlin/Native вызывает функцию objc_release, которая освобождает эту ссылку на объект Objective-C.
Ранее менеджер памяти Kotlin/Native запускал objc_release в специальном потоке GC. Если это последняя ссылка на объект, объект освобождается. Проблемы могли возникнуть, если у объектов Objective-C есть пользовательские хуки освобождения, такие как метод dealloc в Objective-C или блок deinit в Swift, и эти хуки требуют вызова в определенном потоке.
Поскольку хуки для объектов в главном потоке обычно должны вызываться именно там, среда выполнения Kotlin/Native теперь также вызывает objc_release в главном потоке. Это должно покрыть случаи, когда объект Objective-C передается в Kotlin в главном потоке и там создается связанный с ним объект Kotlin. Это работает, только если обрабатывается главная очередь диспетчеризации, как в обычных приложениях с пользовательским интерфейсом. Если используется не главная очередь или объект передан в Kotlin не из главного потока, objc_release, как и прежде, вызывается в специальном потоке GC.
Как отключить
Если возникнут проблемы, это поведение можно отключить в файле gradle.properties с помощью следующего параметра:
kotlin.native.binary.objcDisposeOnMain=false
Не стесняйтесь сообщать о таких случаях в наш трекер проблем.
Отсутствие инициализации объектов при доступе к константным значениям в Kotlin/Native
Начиная с Kotlin 1.9.0, бэкенд Kotlin/Native не инициализирует объекты при обращении к полям const val:
object MyObject {
init {
println("side effect!")
}
const val y = 1
}
fun main() {
println(MyObject.y) // No initialization at first
val x = MyObject // Initialization occurs
println(x.y)
}
Теперь поведение унифицировано с Kotlin/JVM, где реализация согласуется с Java и объекты в этом случае никогда не инициализируются. Благодаря этому изменению можно также ожидать повышения производительности проектов Kotlin/Native.
Возможность настройки автономного режима для тестов в симуляторе iOS в Kotlin/Native
По умолчанию при запуске тестов Kotlin/Native в симуляторе iOS используется флаг --standalone, позволяющий избежать ручного запуска и выключения симулятора. В версии 1.9.0 теперь можно настроить использование этого флага в задаче Gradle с помощью свойства standalone. По умолчанию используется флаг --standalone, поэтому автономный режим включен.
Вот пример отключения автономного режима в файле build.gradle.kts:
tasks.withType<org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeSimulatorTest>().configureEach {
standalone.set(false)
}
Связывание библиотек в Kotlin/Native
Начиная с Kotlin 1.9.0, компилятор Kotlin/Native обрабатывает проблемы связывания в библиотеках Kotlin так же, как Kotlin/JVM. Такие проблемы могут возникнуть, если автор сторонней библиотеки Kotlin вносит несовместимые изменения в экспериментальные API, используемые другой сторонней библиотекой Kotlin.
Теперь при проблемах связывания между сторонними библиотеками Kotlin сборка не завершается с ошибкой во время компиляции. Вместо этого такие ошибки возникают только во время выполнения, как и на JVM.
Компилятор Kotlin/Native сообщает предупреждения каждый раз, когда обнаруживает проблемы со связыванием библиотек. Эти предупреждения можно найти в журналах компиляции, например:
No function found for symbol 'org.samples/MyRemovedClass.doSomething|3657632771909858561[0]' Can not get instance of singleton 'MyEnumClass.REMOVED_ENTRY': No enum entry found for symbol 'org.samples/MyEnumClass.REMOVED_ENTRY|null[0]' Function 'getMyRemovedClass' can not be called: Function uses unlinked class symbol 'org.samples/MyRemovedClass|null[0]'
В проектах это поведение можно дополнительно настроить или даже отключить:
Если вы не хотите видеть эти предупреждения в журналах компиляции, подавите их с помощью параметра компилятора
-Xpartial-linkage-loglevel=INFO.Также можно повысить уровень серьезности предупреждений до ошибок компиляции с помощью
-Xpartial-linkage-loglevel=ERROR. В этом случае компиляция завершится с ошибкой, и все ошибки будут отображены в журнале компиляции. Используйте этот параметр для более подробного изучения проблем связывания.Если возникнут неожиданные проблемы с этой возможностью, ее всегда можно отключить с помощью параметра компилятора
-Xpartial-linkage=disable. Не стесняйтесь сообщать о таких случаях в наш трекер проблем.
// An example of passing compiler options via Gradle build file.
kotlin {
macosX64("native") {
binaries.executable()
compilations.configureEach {
compilerOptions.configure {
// To suppress linkage warnings:
freeCompilerArgs.add("-Xpartial-linkage-loglevel=INFO")
// To raise linkage warnings to errors:
freeCompilerArgs.add("-Xpartial-linkage-loglevel=ERROR")
// To disable the feature completely:
freeCompilerArgs.add("-Xpartial-linkage=disable")
}
}
}
}
Параметр компилятора для неявных преобразований целых чисел при взаимодействии с C
Мы добавили параметр компилятора для взаимодействия с C, позволяющий использовать неявные преобразования целых чисел. После тщательного рассмотрения мы решили добавить этот параметр, чтобы предотвратить непреднамеренное использование, поскольку эта возможность все еще нуждается в улучшении, а наша цель — API высочайшего качества.
В этом примере кода неявное преобразование целого числа позволяет выполнить options = 0, хотя options имеет беззнаковый тип UInt, а 0 — знаковый.
val today = NSDate()
val tomorrow = NSCalendar.currentCalendar.dateByAddingUnit(
unit = NSCalendarUnitDay,
value = 1,
toDate = today,
options = 0
)
Чтобы использовать неявные преобразования с библиотеками взаимодействия с нативным кодом, передайте компилятору параметр -XXLanguage:+ImplicitSignedToUnsignedIntegerConversion.
Это можно настроить в файле Gradle build.gradle.kts:
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile>().configureEach {
compilerOptions.freeCompilerArgs.addAll(
"-XXLanguage:+ImplicitSignedToUnsignedIntegerConversion"
)
}
Kotlin Multiplatform
В Kotlin Multiplatform в версии 1.9.0 появились важные обновления, призванные улучшить опыт разработки:
Новая структура исходных наборов Android включена по умолчанию
Предварительная поддержка кэша конфигурации Gradle в мультиплатформенных проектах
Изменения в поддержке целевой платформы Android
Мы продолжаем работу над стабилизацией Kotlin Multiplatform. Важный шаг в этом направлении — обеспечение полноценной поддержки целевой платформы Android. Мы рады сообщить, что в будущем команда Android из Google предоставит собственный плагин Gradle для поддержки Android в Kotlin Multiplatform.
Чтобы подготовить почву для нового решения от Google, в версии 1.9.0 мы переименовываем блок android в текущем Kotlin DSL. Замените все вхождения блока android на androidTarget в сценариях сборки. Это временное изменение необходимо, чтобы освободить имя android для будущего DSL от Google.
Плагин Google станет предпочтительным способом работы с Android в мультиплатформенных проектах. Когда он будет готов, мы предоставим необходимые инструкции по миграции, чтобы вы могли, как и прежде, использовать короткое имя android.
Новая структура исходных наборов Android включена по умолчанию
Начиная с Kotlin 1.9.0 новая структура исходных наборов Android используется по умолчанию. Она заменила прежнюю схему именования каталогов, которая во многих отношениях была неудобной. У новой структуры есть ряд преимуществ:
Упрощенная семантика типов — новая структура исходных наборов Android обеспечивает четкие и последовательные правила именования, помогающие различать разные типы исходных наборов.
Улучшенная структура каталогов с исходным кодом — новая структура делает расположение
SourceDirectoriesболее логичным, упрощая организацию кода и поиск исходных файлов.Понятная схема именования конфигураций Gradle — теперь схема более последовательна и предсказуема как в
KotlinSourceSets, так и вAndroidSourceSets.
Для новой структуры требуется Android Gradle plugin версии 7.0 или выше; она поддерживается в Android Studio 2022.3 и более поздних версиях. Ознакомьтесь с нашим руководством по миграции, чтобы внести необходимые изменения в файл build.gradle(.kts).
Предварительная поддержка кэша конфигурации Gradle
В Kotlin 1.9.0 появилась поддержка кэша конфигурации Gradle в мультиплатформенных библиотеках. Если вы разработчик библиотеки, вы уже можете воспользоваться повышением производительности сборки.
Кэш конфигурации Gradle ускоряет процесс сборки, повторно используя результаты фазы конфигурации при последующих сборках. Начиная с Gradle 8.1 эта возможность имеет стабильный статус. Чтобы включить ее, следуйте инструкциям в документации Gradle.
Kotlin/Wasm
Команда Kotlin продолжает экспериментировать с новой целевой платформой Kotlin/Wasm. В этом выпуске представлены оптимизации производительности и оптимизации размера, а также обновления взаимодействия с JavaScript.
Оптимизации размера
В Kotlin 1.9.0 значительно уменьшен размер проектов WebAssembly (Wasm). При сравнении двух проектов «Hello World» размер кода для Wasm в Kotlin 1.9.0 теперь более чем в 10 раз меньше, чем в Kotlin 1.8.20.
Эти оптимизации размера позволяют эффективнее использовать ресурсы и повышают производительность при создании кода Kotlin для платформ Wasm.
Обновления взаимодействия с JavaScript
В этом выпуске Kotlin внесены изменения во взаимодействие Kotlin и JavaScript для Kotlin/Wasm. Поскольку Kotlin/Wasm — это экспериментальная возможность, на ее взаимодействие распространяются определенные ограничения.
Ограничение типов Dynamic
Начиная с версии 1.9.0 Kotlin больше не поддерживает использование типов Dynamic в Kotlin/Wasm. Этот тип объявлен устаревшим; вместо него рекомендуется использовать новый универсальный тип JsAny, который упрощает взаимодействие с JavaScript.
Подробнее см. в документации по взаимодействию Kotlin/Wasm с JavaScript.
Ограничение типов, не помеченных как external
Kotlin/Wasm поддерживает преобразование определенных статических типов Kotlin при передаче значений в JavaScript и обратно. К поддерживаемым типам относятся:
Примитивные типы, например знаковые числа,
BooleanиChar.String.Функциональные типы.
Другие типы передавались без преобразования в виде непрозрачных ссылок, что приводило к несоответствиям между подтипами JavaScript и Kotlin.
Чтобы решить эту проблему, Kotlin ограничивает взаимодействие с JavaScript набором типов с полноценной поддержкой. Начиная с Kotlin 1.9.0, во взаимодействии Kotlin/Wasm с JavaScript поддерживаются только типы external, примитивные типы, строки и функциональные типы. Кроме того, был введен отдельный явный тип JsReference для представления дескрипторов объектов Kotlin/Wasm, которые можно использовать при взаимодействии с JavaScript.
Подробнее см. в документации по взаимодействию Kotlin/Wasm с JavaScript.
Kotlin/Wasm в Kotlin Playground
Kotlin Playground поддерживает целевую платформу Kotlin/Wasm. Вы можете писать, запускать и публиковать код Kotlin для Kotlin/Wasm. Попробуйте!
import kotlin.time.*
import kotlin.time.measureTime
fun main() {
println("Hello from Kotlin/Wasm!")
computeAck(3, 10)
}
tailrec fun ack(m: Int, n: Int): Int = when {
m == 0 -> n + 1
n == 0 -> ack(m - 1, 1)
else -> ack(m - 1, ack(m, n - 1))
}
fun computeAck(m: Int, n: Int) {
var res = 0
val t = measureTime {
res = ack(m, n)
}
println()
println("ack($m, $n) = ${res}")
println("duration: ${t.inWholeNanoseconds / 1e6} ms")
}
Kotlin/JS
В этом выпуске представлены обновления Kotlin/JS, в том числе удаление старого компилятора Kotlin/JS, объявление плагина Kotlin/JS Gradle устаревшим и экспериментальная поддержка ES2015:
Удаление старого компилятора Kotlin/JS
В Kotlin 1.8.0 мы объявили, что бэкенд на основе IR получил статус стабильного. С тех пор отсутствие явного указания компилятора стало считаться ошибкой, а использование старого компилятора приводило к предупреждениям.
В Kotlin 1.9.0 использование старого бэкенда приводит к ошибке. Перейдите на компилятор IR.
Устаревание плагина Kotlin/JS Gradle
Начиная с Kotlin 1.9.0, плагин Gradle kotlin-js объявлен устаревшим. Рекомендуем вместо него использовать плагин Gradle kotlin-multiplatform с целевой платформой js().
Функциональность плагина Kotlin/JS Gradle в основном дублировала плагин kotlin-multiplatform, а внутри они использовали одну и ту же реализацию. Это пересечение вызывало путаницу и увеличивало нагрузку на команду Kotlin по сопровождению.
Инструкции по миграции приведены в нашем руководстве по совместимости Kotlin Multiplatform. Если вы обнаружите проблемы, не описанные в руководстве, сообщите о них в наш трекер задач.
Устаревание external enum
В Kotlin 1.9.0 использование внешних перечислений будет объявлено устаревшим из-за проблем со статическими элементами перечислений, такими как entries, которые не могут существовать вне Kotlin. Вместо этого рекомендуем использовать внешний запечатанный класс с подклассами-объектами:
// Before
external enum class ExternalEnum { A, B }
// After
external sealed class ExternalEnum {
object A: ExternalEnum
object B: ExternalEnum
}
Заменив внешнее перечисление внешним запечатанным классом с подклассами-объектами, вы получите аналогичную функциональность и избежите проблем, связанных с методами по умолчанию.
Начиная с Kotlin 1.9.0, использование внешних перечислений будет помечаться как устаревшее. Для совместимости и упрощения дальнейшего сопровождения рекомендуем обновить код и использовать предложенную реализацию внешнего запечатанного класса.
Экспериментальная поддержка классов и модулей ES2015
В этом выпуске появилась экспериментальная поддержка модулей ES2015 и генерации классов ES2015:
Модули позволяют упростить кодовую базу и облегчить ее сопровождение.
Классы позволяют применять принципы объектно-ориентированного программирования (ООП), делая код более понятным и наглядным.
Чтобы включить эти возможности, соответствующим образом обновите файл build.gradle.kts:
// build.gradle.kts
kotlin {
js(IR) {
useEsModules() // Enables ES2015 modules
browser()
}
}
// Enables ES2015 classes generation
tasks.withType<KotlinJsCompile>().configureEach {
kotlinOptions {
useEsClasses = true
}
}
Подробнее об ES2015 (ECMAScript 2015, ES6) — в официальной документации.
Изменение стандартного каталога дистрибутива JS для production-сборки
До Kotlin 1.9.0 целевой каталог дистрибутива назывался build/distributions. Однако это стандартный каталог для архивов Gradle. Чтобы решить эту проблему, в Kotlin 1.9.0 мы изменили стандартный целевой каталог дистрибутива на build/dist/<targetName>/<binaryName>.
Например, productionExecutable находился в build/distributions. В Kotlin 1.9.0 он находится в build/dist/js/productionExecutable.
Перенос объявлений org.w3c из stdlib-js
Начиная с Kotlin 1.9.0, stdlib-js больше не содержит объявления org.w3c. Вместо этого эти объявления перенесены в отдельную зависимость Gradle. Когда вы добавите плагин Kotlin Multiplatform Gradle в файл build.gradle.kts, эти объявления будут автоматически включены в проект, как и стандартная библиотека.
Вручную выполнять какие-либо действия или миграцию не нужно. Все необходимые изменения будут внесены автоматически.
Gradle
В Kotlin 1.9.0 появились новые параметры компилятора Gradle и множество других изменений:
Удаление свойства classpath
В Kotlin 1.7.0 мы объявили о начале цикла устаревания свойства задачи KotlinCompile: classpath. В Kotlin 1.8.0 уровень устаревания был повышен до ERROR. В этом выпуске свойство classpath наконец удалено. Теперь во всех задачах компиляции для передачи списка необходимых для компиляции библиотек следует использовать входной параметр libraries.
Новые параметры компилятора
Теперь плагин Kotlin Gradle предоставляет новые свойства для opt-in и прогрессивного режима компилятора.
Чтобы включить поддержку новых API, теперь можно использовать свойство
optInи передать список строк, например:optIn.set(listOf(a, b, c)).Чтобы включить прогрессивный режим, используйте
progressiveMode.set(true).
Параметры компилятора для Kotlin/JVM на уровне проекта
Начиная с Kotlin 1.9.0, внутри блока конфигурации kotlin доступен новый блок compilerOptions:
kotlin {
compilerOptions {
jvmTarget.set(JVM.Target_11)
}
}
Это значительно упрощает настройку параметров компилятора. Однако важно учитывать несколько особенностей:
Эта конфигурация работает только на уровне проекта.
Для плагина Android этот блок настраивает тот же объект, что и:
android {
kotlinOptions {}
}
Блоки конфигурации
android.kotlinOptionsиkotlin.compilerOptionsпереопределяют друг друга. Всегда применяется последний (нижний) блок в файле сборки.Если
moduleNameнастроен на уровне проекта, его значение может измениться при передаче компилятору. Для компиляцииmainэтого не происходит, но для других типов, например исходных файлов тестов, плагин Kotlin Gradle добавит суффикс_test.Конфигурация внутри
tasks.withType<KotlinJvmCompile>().configureEach {}(илиtasks.named<KotlinJvmCompile>("compileKotlin") { }) переопределяет иkotlin.compilerOptions, иandroid.kotlinOptions.
Параметр компилятора для имени модуля Kotlin/Native
Параметр компилятора Kotlin/Native module-name теперь легко доступен в плагине Kotlin Gradle.
Этот параметр задает имя модуля компиляции, а также позволяет добавлять префикс к именам объявлений, экспортируемых в Objective-C.
Теперь имя модуля можно задать непосредственно в блоке compilerOptions файлов сборки Gradle:
tasks.named<org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile>("compileKotlinLinuxX64") {
compilerOptions {
moduleName.set("my-module-name")
}
}
tasks.named("compileKotlinLinuxX64", org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile.class) {
compilerOptions {
moduleName = "my-module-name"
}
}
Отдельные плагины компилятора для официальных библиотек Kotlin
В Kotlin 1.9.0 представлены отдельные плагины компилятора для официальных библиотек. Раньше плагины компилятора встраивались в соответствующие плагины Gradle. Это могло приводить к проблемам совместимости, если плагин компилятора был собран для версии Kotlin выше, чем версия Kotlin runtime при сборке Gradle.
Теперь плагины компилятора добавляются как отдельные зависимости, поэтому проблем совместимости со старыми версиями Gradle больше не будет. Еще одно важное преимущество нового подхода — возможность использовать новые плагины компилятора с другими системами сборки, например Bazel.
Ниже приведен список новых плагинов компилятора, которые теперь публикуются в Maven Central:
kotlin-atomicfu-compiler-plugin
kotlin-allopen-compiler-plugin
kotlin-lombok-compiler-plugin
kotlin-noarg-compiler-plugin
kotlin-sam-with-receiver-compiler-plugin
kotlinx-serialization-compiler-plugin
У каждого плагина есть соответствующий вариант -embeddable. Например, kotlin-allopen-compiler-plugin-embeddable предназначен для работы с артефактом kotlin-compiler-embeddable — стандартным вариантом для артефактов скриптов.
Gradle добавляет эти плагины как аргументы компилятора. Вносить изменения в существующие проекты не требуется.
Повышение минимальной поддерживаемой версии
Начиная с Kotlin 1.9.0 минимальная поддерживаемая версия Android Gradle plugin — 4.2.2.
См. документацию о совместимости плагина Kotlin Gradle с доступными версиями Gradle.
kapt не приводит к преждевременному созданию задач в Gradle
До версии 1.9.0 плагин компилятора kapt приводил к преждевременному созданию задач, запрашивая настроенный экземпляр задачи компиляции Kotlin. Это поведение исправлено в Kotlin 1.9.0. Если для файла build.gradle.kts используется конфигурация по умолчанию, это изменение вас не затронет.
Подробнее см. в задаче YouTrack.
Программная настройка режима проверки целевой платформы JVM
До Kotlin 1.9.0 существовал только один способ настроить обнаружение несовместимости целевых платформ JVM для Kotlin и Java. Для всего проекта нужно было задать kotlin.jvm.target.validation.mode=ERROR в файле gradle.properties.
Теперь этот параметр также можно настроить на уровне задачи в файле build.gradle.kts:
tasks.named<org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile>("compileKotlin") {
jvmTargetValidationMode.set(org.jetbrains.kotlin.gradle.dsl.jvm.JvmTargetValidationMode.WARNING)
}
Стандартная библиотека
В Kotlin 1.9.0 появились важные улучшения стандартной библиотеки:
Оператор
..<и API для работы со временем получили статус Stable.Стандартная библиотека Kotlin/Native была тщательно проверена и обновлена
Аннотацию
@Volatileможно использовать на большем количестве платформПоявилась общая функция для получения группы захвата регулярного выражения по имени
Для форматирования и разбора шестнадцатеричных чисел добавлен класс
HexFormat
Стабильный оператор ..< для диапазонов с открытым концом
Новый оператор ..< для диапазонов с открытым концом был представлен в Kotlin 1.7.20 и получил статус Stable в версии 1.8.0. В версии 1.9.0 статус Stable также получил API стандартной библиотеки для работы с диапазонами с открытым концом.
Наше исследование показывает, что новый оператор ..< позволяет лучше понять, когда объявлен диапазон с открытым концом. При использовании инфиксной функции until легко ошибочно предположить, что верхняя граница включена в диапазон.
Вот пример использования функции until:
fun main() {
for (number in 2 until 10) {
if (number % 2 == 0) {
print("$number ")
}
}
// 2 4 6 8
}
А вот пример использования нового оператора ..<:
fun main() {
for (number in 2..<10) {
if (number % 2 == 0) {
print("$number ")
}
}
// 2 4 6 8
}
Подробнее о возможностях этого оператора см. в разделе Что нового в Kotlin 1.7.20.
Стабильный API для работы со временем
Начиная с версии 1.3.50 мы представляли предварительные версии нового API для измерения времени. API для работы с длительностью получил статус Stable в версии 1.6.0. В версии 1.9.0 оставшаяся часть API для измерения времени также получила статус Stable.
Старый API для работы со временем предоставлял функции measureTimeMillis и measureNanoTime, которые не очень понятны в использовании. Хотя очевидно, что они измеряют время в разных единицах, неясно, что measureTimeMillis использует для измерения времени системные часы реального времени, а measureNanoTime использует монотонный источник времени. Новый API для работы со временем решает эту и другие проблемы, делая API удобнее для пользователей.
С помощью нового API для работы со временем можно легко:
Измерить время выполнения фрагмента кода, используя монотонный источник времени и нужную единицу измерения.
Отметить момент времени.
Сравнить два момента времени и найти разницу между ними.
Проверить, сколько времени прошло с определённого момента.
Проверить, наступил ли определённый момент времени.
Измерение времени выполнения кода
Чтобы измерить время выполнения блока кода, используйте встроенную функцию measureTime.
Чтобы измерить время выполнения блока кода и получить результат выполнения этого блока, используйте встроенную функцию measureTimedValue.
По умолчанию обе функции используют монотонный источник времени. Однако при желании можно использовать источник прошедшего реального времени. Например, на Android источник времени по умолчанию System.nanoTime() учитывает только время, пока устройство активно. Отсчёт времени приостанавливается, когда устройство переходит в режим глубокого сна. Чтобы учитывать время, пока устройство находится в режиме глубокого сна, можно создать источник времени, использующий SystemClock.elapsedRealtimeNanos():
object RealtimeMonotonicTimeSource : AbstractLongTimeSource(DurationUnit.NANOSECONDS) {
override fun read(): Long = SystemClock.elapsedRealtimeNanos()
}
Отметка моментов времени и измерение разницы между ними
Чтобы отметить определённый момент времени, используйте интерфейс TimeSource и функцию markNow() для создания объекта TimeMark. Чтобы измерить разницу между объектами TimeMarks из одного источника времени, используйте оператор вычитания (-):
import kotlin.time.*
fun main() {
val timeSource = TimeSource.Monotonic
val mark1 = timeSource.markNow()
Thread.sleep(500) // Sleep 0.5 seconds.
val mark2 = timeSource.markNow()
repeat(4) { n ->
val mark3 = timeSource.markNow()
val elapsed1 = mark3 - mark1
val elapsed2 = mark3 - mark2
println("Measurement 1.${n + 1}: elapsed1=$elapsed1, elapsed2=$elapsed2, diff=${elapsed1 - elapsed2}")
}
// It's also possible to compare time marks with each other.
println(mark2 > mark1) // This is true, as mark2 was captured later than mark1.
}
Чтобы проверить, прошёл ли срок или истёк ли тайм-аут, используйте функции-расширения hasPassedNow() и hasNotPassedNow():
import kotlin.time.*
import kotlin.time.Duration.Companion.seconds
fun main() {
val timeSource = TimeSource.Monotonic
val mark1 = timeSource.markNow()
val fiveSeconds: Duration = 5.seconds
val mark2 = mark1 + fiveSeconds
// It hasn't been 5 seconds yet
println(mark2.hasPassedNow())
// false
// Wait six seconds
Thread.sleep(6000)
println(mark2.hasPassedNow())
// true
}
Путь стандартной библиотеки Kotlin/Native к стабильности
Стандартная библиотека Kotlin/Native продолжает развиваться, и мы решили, что пришло время провести полную проверку, чтобы убедиться, что она соответствует нашим высоким стандартам. В рамках этой проверки мы тщательно рассмотрели каждую существующую публичную сигнатуру. Для каждой сигнатуры мы оценивали, соответствует ли она следующим критериям:
Имеет уникальное назначение.
Согласуется с другими API Kotlin.
Имеет поведение, аналогичное соответствующему API для JVM.
Рассчитана на дальнейшее развитие.
По результатам этой оценки мы приняли одно из следующих решений:
Присвоили статус Stable.
Присвоили статус Experimental.
Пометили как
private.Изменили поведение.
Переместили в другое расположение.
Пометили как устаревшее.
Пометили как obsolete.
Здесь мы не будем перечислять все результаты проверки, но расскажем о некоторых наиболее важных:
Мы присвоили API Atomics статус Stable.
Мы присвоили
kotlinx.cinteropстатус Experimental и теперь требуем отдельного согласия на использование этого пакета. Подробнее см. в разделе Явные гарантии стабильности взаимодействия с C.Мы пометили класс
Workerи связанные с ним API как obsolete.Мы пометили класс
BitSetкак obsolete.Мы пометили все API
publicв пакетеkotlin.native.internalкакprivateили переместили их в другие пакеты.
Явные гарантии стабильности взаимодействия с C
Чтобы поддерживать высокое качество нашего API, мы решили присвоить kotlinx.cinterop статус Experimental. Хотя kotlinx.cinterop прошло тщательную проверку и хорошо зарекомендовало себя, до присвоения статуса Stable ещё есть возможности для улучшения. Мы рекомендуем использовать этот API для взаимодействия, но ограничивать его применение конкретными областями ваших проектов. Это упростит переход, когда мы начнём развивать этот API, чтобы присвоить ему статус Stable.
Чтобы использовать внешние API, подобные C, например указатели, необходимо явно согласиться с использованием @OptIn(ExperimentalForeignApi), иначе код не скомпилируется.
Чтобы использовать остальные возможности kotlinx.cinterop, предназначенные для взаимодействия с Objective-C/Swift, необходимо явно согласиться с использованием @OptIn(BetaInteropApi). Если попытаться использовать этот API без такого согласия, код скомпилируется, но компилятор выдаст предупреждения с чётким объяснением ожидаемого поведения.
Подробнее об этих аннотациях см. в исходном коде Annotations.kt.
Подробнее обо всех изменениях, внесённых в рамках этой проверки, см. в задаче YouTrack.
Будем рады вашим отзывам! Оставить отзыв можно прямо в задаче.
Стабильная аннотация @Volatile
Если аннотировать свойство var с помощью @Volatile, его поле хранения будет помечено таким образом, что все операции чтения и записи этого поля станут атомарными, а результаты записи всегда будут видны другим потокам.
До версии 1.8.20 аннотация kotlin.jvm.Volatile была доступна в общей стандартной библиотеке. Однако эта аннотация действовала только на JVM. При использовании на других платформах она игнорировалась, что приводило к ошибкам.
В версии 1.8.20 мы представили экспериментальную общую аннотацию kotlin.concurrent.Volatile, предварительную версию которой можно было попробовать как на JVM, так и в Kotlin/Native.
В версии 1.9.0 kotlin.concurrent.Volatile получила статус Stable. Если вы используете kotlin.jvm.Volatile в мультиплатформенных проектах, рекомендуем перейти на kotlin.concurrent.Volatile.
Новая общая функция для получения группы захвата регулярного выражения по имени
До версии 1.9.0 на каждой платформе было собственное расширение для получения группы захвата регулярного выражения по её имени из результата сопоставления с регулярным выражением. Однако общей функции не было. До Kotlin 1.8.0 создать общую функцию было невозможно, поскольку стандартная библиотека всё ещё поддерживала цели JVM 1.6 и 1.7.
Начиная с Kotlin 1.8.0 стандартная библиотека компилируется с целевой версией JVM 1.8. Поэтому в версии 1.9.0 появилась общая функция groups, позволяющая получить содержимое группы по её имени из результата сопоставления с регулярным выражением. Это удобно, когда нужно получить результаты сопоставления, относящиеся к определённой группе захвата.
Вот пример регулярного выражения с тремя группами захвата: city, state и areaCode. Эти имена групп можно использовать для доступа к найденным значениям:
fun main() {
val regex = """\b(?<city>[A-Za-z\s]+),\s(?<state>[A-Z]{2}):\s(?<areaCode>[0-9]{3})\b""".toRegex()
val input = "Coordinates: Austin, TX: 123"
val match = regex.find(input)!!
println(match.groups["city"]?.value)
// Austin
println(match.groups["state"]?.value)
// TX
println(match.groups["areaCode"]?.value)
// 123
}
Новая функция для создания родительских каталогов по пути
В версии 1.9.0 появилась новая функция-расширение createParentDirectories(), позволяющая создать новый файл вместе со всеми необходимыми родительскими каталогами. Если передать путь к файлу в createParentDirectories(), функция проверит, существуют ли родительские каталоги. Если существуют, она ничего не сделает. Если нет, она создаст их.
createParentDirectories() особенно полезна при копировании файлов. Например, её можно использовать вместе с функцией copyToRecursively():
sourcePath.copyToRecursively( destinationPath.createParentDirectories(), followLinks = false )
Новый класс HexFormat для форматирования и разбора шестнадцатеричных чисел
В версии 1.9.0 класс HexFormat и связанные с ним функции-расширения доступны как экспериментальная возможность, позволяющая преобразовывать числовые значения в шестнадцатеричные строки и обратно. В частности, с помощью функций-расширений можно преобразовывать шестнадцатеричные строки в ByteArrays и другие числовые типы (Int, Short, Long) и обратно.
Например:
println(93.toHexString()) // "0000005d"
Класс HexFormat содержит параметры форматирования, которые можно настроить с помощью конструктора HexFormat{}.
При работе с ByteArrays доступны следующие параметры, которые настраиваются с помощью свойств:
Параметр |
Описание |
|---|---|
|
Регистр шестнадцатеричных цифр: верхний или нижний. По умолчанию используется нижний регистр. |
|
Максимальное количество байтов в строке. |
|
Максимальное количество байтов в группе. |
|
Разделитель байтов. По умолчанию отсутствует. |
|
Строка, которая непосредственно предшествует двухзначному шестнадцатеричному представлению каждого байта. По умолчанию отсутствует. |
|
Строка, которая непосредственно следует за двухзначным шестнадцатеричным представлением каждого байта. По умолчанию отсутствует. |
Например:
val macAddress = "001b638445e6".hexToByteArray()
// Use HexFormat{} builder to separate the hexadecimal string by colons
println(macAddress.toHexString(HexFormat { bytes.byteSeparator = ":" }))
// "00:1b:63:84:45:e6"
// Use HexFormat{} builder to:
// * Make the hexadecimal string uppercase
// * Group the bytes in pairs
// * Separate by periods
val threeGroupFormat = HexFormat { upperCase = true; bytes.bytesPerGroup = 2; bytes.groupSeparator = "." }
println(macAddress.toHexString(threeGroupFormat))
// "001B.6384.45E6"
При работе с числовыми типами доступны следующие параметры, которые настраиваются с помощью свойств:
Параметр |
Описание |
|---|---|
|
Префикс шестнадцатеричной строки. По умолчанию отсутствует. |
|
Суффикс шестнадцатеричной строки. По умолчанию отсутствует. |
|
Удалять ли начальные нули в шестнадцатеричной строке. По умолчанию начальные нули не удаляются. |
Например:
// Use HexFormat{} builder to parse a hexadecimal that has prefix: "0x".
println("0x3a".hexToInt(HexFormat { number.prefix = "0x" })) // "58"
Обновления документации
В документации Kotlin появились важные изменения:
Тур по Kotlin — изучите основы языка программирования Kotlin в главах, включающих теорию и практические задания.
Структура исходных наборов Android — узнайте о новой структуре исходных наборов Android.
Руководство по совместимости Kotlin Multiplatform — узнайте о несовместимых изменениях, с которыми можно столкнуться при разработке проектов на Kotlin Multiplatform.
Kotlin Wasm — узнайте о Kotlin/Wasm и о том, как использовать его в проектах Kotlin Multiplatform.
Установка Kotlin 1.9.0
Проверьте версию IDE
IntelliJ IDEA 2022.3.3 и 2023.1.1 автоматически предложат обновить плагин Kotlin до версии 1.9.0. В IntelliJ IDEA 2023.2 будет включён плагин Kotlin 1.9.0.
В ближайших выпусках Android Studio Giraffe (223) и Hedgehog (231) появится поддержка Kotlin 1.9.0.
Новый компилятор командной строки можно скачать на странице релиза на GitHub.
Настройка параметров Gradle
Чтобы загружать артефакты и зависимости Kotlin, обновите файл settings.gradle(.kts) и укажите в нём репозиторий Maven Central:
pluginManagement {
repositories {
mavenCentral()
gradlePluginPortal()
}
}
Если репозиторий не указан, Gradle использует закрытый репозиторий JCenter, что может привести к проблемам с артефактами Kotlin.
Руководство по совместимости для Kotlin 1.9.0
Kotlin 1.9.0 — это выпуск с новыми функциональными возможностями, поэтому он может содержать изменения, несовместимые с кодом, написанным для более ранних версий языка. Подробный список этих изменений см. в руководстве по совместимости для Kotlin 1.9.0.
© 2010–2026 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/whatsnew19.html