Что нового в Kotlin 1.9.0
Дата выпуска: 6 июля 2023 года
Вышла версия Kotlin 1.9.0, и компилятор K2 для JVM теперь в бета-версии. Кроме того, вот некоторые ключевые моменты:
Новая общая функция для получения группы захвата регулярного выражения по имени
Предварительный просмотр кеша конфигурации Gradle в 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. Вы также можете выполнить следующую команду:
./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:


Вы также можете найти используемую версию 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 мы стабилизировали некоторые новые возможности языка, которые были введены ранее:
Стабильная замена функции enum class values
В 1.8.20 свойство entries для классов перечислений было введено как экспериментальная функция. Свойство 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 для классов перечислений см. в Новом в Kotlin 1.8.20.
Стабильные объекты данных для симметрии с классами данных
Объявления объектов данных, которые были введены в 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.
Поддержка вторичных конструкторов с телами в инлайновых классах значений
Начиная с Kotlin 1.9.0, использование вторичных конструкторов с телами в инлайновых классах значений доступно по умолчанию:
@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 допускал только публичные первичные конструкторы в инлайновых классах. В результате было невозможно инкапсулировать основополагающие значения или создать инлайновый класс, который бы представлял некоторые ограниченные значения.
По мере развития Kotlin эти проблемы были решены. Kotlin 1.4.30 снял ограничения на блоки init, а затем Kotlin 1.8.20 представил предварительный просмотр вторичных конструкторов с телами. Они теперь доступны по умолчанию. Подробнее об эволюции инлайновых классов 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 там. Способ, которым менеджер памяти Kotlin/Native ранее обрабатывал ссылки на объекты Objective-C, мог приводить к утечкам памяти. Мы считаем, что новое поведение должно улучшить надёжность менеджера памяти.
Рассмотрим объект 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
По умолчанию при запуске тестов iOS-симулятора для Kotlin/Native используется флаг --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-взаимодействии
Продолжая работу над стабилизацией Kotlin/Native, мы представили параметр компилятора для 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 Многоплатформенность
Kotlin Многоплатформенность получила ряд значительных обновлений в версии 1.9.0, призванных улучшить ваш опыт разработки:
Новая структура наборов исходных файлов Android по умолчанию
Предварительный просмотр кэша конфигурации Gradle в многоплатформенных проектах
Изменения в поддержке платформы Android
Мы продолжаем работу по стабилизации Kotlin Многоплатформенность. Важным шагом является обеспечение первоклассной поддержки платформы Android. Мы рады объявить, что в будущем команда Android из Google предоставит свой собственный плагин Gradle для поддержки Android в Kotlin Многоплатформенность.
Чтобы открыть путь для этого нового решения от Google, мы переименовали блок android в текущем Kotlin DSL в версии 1.9.0. Пожалуйста, замените все вхождения блока android на androidTarget в ваших скриптах сборки. Это временное изменение, необходимое для освобождения имени android для предстоящего DSL от Google.
Плагин Google станет предпочтительным способом работы с Android в многоплатформенных проектах. Когда он будет готов, мы предоставим необходимые инструкции по миграции, чтобы вы могли использовать краткое имя android как и прежде.
Новая структура наборов исходных файлов Android по умолчанию
Начиная с Kotlin 1.9.0, новая структура наборов исходных файлов Android установлена по умолчанию. Она заменила предыдущую схему именования каталогов, которая была неоднозначной. Новая структура имеет ряд преимуществ:
Упрощенная семантика типов — Новая структура наборов исходных файлов Android обеспечивает ясные и согласованные соглашения об именовании, которые помогают различать различные типы наборов исходных файлов.
Улучшенная структура каталогов исходных файлов — С новой структурой расположение
SourceDirectoriesстановится более согласованным, что упрощает организацию кода и поиск исходных файлов.Ясная схема именования конфигураций Gradle — Схема стала более согласованной и предсказуемой как в
KotlinSourceSets, так и вAndroidSourceSets.
Новая структура требует версии плагина Android Gradle 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 является экспериментальной функцией, на его взаимодействие накладываются определённые ограничения.
Ограничение динамических типов
Начиная с версии 1.9.0, Kotlin больше не поддерживает использование типов Dynamic в Kotlin/Wasm. Теперь они устарели в пользу нового универсального типа JsAny, который облегчает взаимодействие с JavaScript.
Для получения более подробной информации см. документацию по взаимодействию Kotlin/Wasm с JavaScript.
Ограничение типов, не являющихся внешними
Kotlin/Wasm поддерживает преобразования для определённых статических типов Kotlin при передаче значений в JavaScript и из него. Эти поддерживаемые типы включают:
Примитивные типы, такие как числа со знаком,
Boolean, иChar.String.Типы функций.
Другие типы передавались без преобразования как неявные ссылки, что приводило к несоответствиям между JavaScript и Kotlin-подтипированием.
Для решения этой проблемы Kotlin ограничивает взаимодействие с JavaScript набором хорошо поддерживаемых типов. Начиная с Kotlin 1.9.0, в Kotlin/Wasm взаимодействии с JavaScript поддерживаются только внешние, примитивные, строковые и функциональные типы. Кроме того, был введен отдельный явный тип 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 Gradle и экспериментальную поддержку ES6:
Устаревание плагина Kotlin/JS Gradle
Начиная с Kotlin 1.9.0, плагин kotlin-js Gradle устарел. Рекомендуется использовать плагин kotlin-multiplatform Gradle с целевым параметром js().
Функциональность плагина Kotlin/JS Gradle в основном дублировала плагин kotlin-multiplatform и использовала одну и ту же реализацию внутри. Это дублирование создавало путаницу и увеличивало нагрузку на команду Kotlin по сопровождению.
Обратитесь к нашему руководству по совместимости Kotlin Multiplatform за инструкциями по миграции. Если вы обнаружите проблемы, которые не охвачены в руководстве, сообщите об этом в нашем трекере ошибок.
Устаревание внешних перечислений
В Kotlin 1.9.0 использование внешних перечислений будет устаревшим из-за проблем со статическими членами перечислений, такими как entries, которые не могут существовать вне Kotlin. Рекомендуется использовать вместо этого внешние запечатанные классы с подклассами типа object:
// Before
external enum class ExternalEnum { A, B }
// After
external sealed class ExternalEnum {
object A: ExternalEnum
object B: ExternalEnum
}
Переход на внешний запечатанный класс с подклассами типа object позволит добиться сходной функциональности внешних перечислений, избежав проблем, связанных с методами по умолчанию.
Начиная с Kotlin 1.9.0, использование внешних перечислений будет помечено как устаревшее. Рекомендуется обновить код для использования предлагаемой реализации внешнего запечатанного класса для совместимости и последующего обслуживания.
Экспериментальная поддержка ES6 классов и модулей
В этом релизе представлена экспериментальная поддержка ES6 модулей и генерации ES6 классов:
Модули предлагают способ упростить вашу кодовую базу и улучшить ее поддержку.
Классы позволяют включить принципы объектно-ориентированного программирования (ООП), что приводит к более чистому и интуитивному коду.
Для активации этих функций необходимо соответствующим образом обновить файл build.gradle.kts.
// build.gradle.kts
kotlin {
js(IR) {
useEsModules() // Enables ES6 modules
browser()
}
}
// Enables ES6 classes generation
tasks.withType<KotlinJsCompile>().configureEach {
kotlinOptions {
useEsClasses = true
}
}
Узнайте больше о ECMAScript 2015 (ES6) в официальной документации.
Изменение целевого каталога для JS-распределения (производство)
До 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. Уровень устаревания был повышен до ERROR в Kotlin 1.8.0. В этом релизе мы окончательно удалили свойство classpath. Все задачи компиляции теперь должны использовать вход libraries для списка библиотек, необходимых для компиляции.
Новые параметры компилятора
Плагин Kotlin Gradle теперь предоставляет новые свойства для включения и прогрессивного режима компилятора.
Для включения новых API вы теперь можете использовать свойство
optInи передавать список строк, например:optIn.set(listOf(a, b, c)).Для включения прогрессивного режима используйте
progressiveMode.set(true).
Параметры компилятора на уровне проекта для Kotlin/JVM
Начиная с Kotlin 1.9.0, доступен новый блок compilerOptions внутри блока конфигурации kotlin.
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 существовал только один способ настройки обнаружения несовместимости между Kotlin и Java в отношении JVM-цели. Вам нужно было установить 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 времени API времени являются стабильными.Стандартная библиотека Kotlin/Native была тщательно пересмотрена и обновлена
Аннотация
@Volatileможет использоваться на большем количестве платформЕсть общая функция для получения группы захвата регулярного выражения по имени
Класс
HexFormatбыл добавлен для форматирования и разбора шестнадцатеричных чисел
Стабильный оператор ..< для открытых диапазонов
Новый оператор ..< для открытых диапазонов, который был представлен в Kotlin 1.7.20 и стал стабильным в 1.8.0. В 1.9.0, 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, связанная с продолжительностью, стала стабильной в 1.6.0. В 1.9.0 остальной API измерения времени стал стабильным.
Старый 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.
Имеет поведение, аналогичное своему аналогу для JVM.
Является устойчивым к будущим изменениям.
На основе этих соображений мы приняли одно из следующих решений:
Сделали его Стабильным.
Сделали его Экспериментальным.
Отмечены как
private.Изменили его поведение.
Перенесли его в другое место.
Устарели его.
Отмечены как устаревшие.
Мы не будем перечислять все результаты обзора здесь, но вот некоторые ключевые моменты:
Мы стабилизировали API атомарных операций.
Мы сделали
kotlinx.cinteropЭкспериментальным, и теперь для использования пакета требуются различные опции включения. Более подробную информацию см. в разделе Явные гарантии стабильности C-совместимости.Мы отметили класс
Workerи его связанные API как устаревшие.Мы отметили класс
BitSetкак устаревший.Мы отметили все
publicAPI в пакетеkotlin.native.internalкакprivateили перенесли их в другие пакеты.
Явные гарантии стабильности C-совместимости
Для поддержания высокого качества нашего API мы решили сделать kotlinx.cinterop Экспериментальным. Несмотря на то, что kotlinx.cinterop было тщательно протестировано, для достижения уровня стабильности ещё есть возможность улучшения. Мы рекомендуем использовать этот API для межплатформенной совместимости, но ограничить его применение конкретными областями в ваших проектах. Это упростит миграцию, когда мы начнём развивать этот API, чтобы сделать его стабильным.
Если вы хотите использовать C-подобные внешние API, такие как указатели, вы должны включить @OptIn(ExperimentalForeignApi), иначе ваш код не будет компилироваться.
Для использования остальной части kotlinx.cinterop, которая охватывает межплатформенную совместимость Objective-C/Swift, вы должны включить @OptIn(BetaInteropApi).
Дополнительную информацию об этих аннотациях можно найти в нашем исходном коде для 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 стала стабильной. Если вы используете аннотацию 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 в проекты Kotlin/Wasm – узнайте о поддерживаемых библиотеках Kotlin для Kotlin/Wasm.
Установка 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–2023 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/whatsnew19.html