Что нового в Kotlin 2.0.20
Вышел Kotlin 2.0.20! В этой версии представлены улучшения производительности и исправления ошибок для Kotlin 2.0.0, в котором мы объявили компилятор Kotlin K2 стабильным. Вот некоторые другие важные изменения в этом выпуске:
Функция copy класса данных будет иметь тот же уровень видимости, что и конструктор
В сборщике мусора для Kotlin/Native появилась возможность конкурентной маркировки
Новая настройка позволяет обмениваться артефактами JVM между проектами Gradle в виде файлов классов
В общую стандартную библиотеку Kotlin добавлена поддержка UUID
Поддержка IDE
Плагины Kotlin с поддержкой версии 2.0.20 включены в последние версии IntelliJ IDEA и Android Studio. Обновлять плагин Kotlin в IDE не нужно. Достаточно изменить версию Kotlin на 2.0.20 в сценариях сборки.
Подробнее см. в разделе Обновление до нового выпуска.
Язык
В Kotlin 2.0.20 начинается внедрение изменений, направленных на повышение согласованности классов данных и замену экспериментальной функции контекстных получателей.
Функция copy класса данных будет иметь тот же уровень видимости, что и конструктор
В настоящее время, если создать класс данных с конструктором private, автоматически сгенерированная функция copy() имеет другой уровень видимости. Впоследствии это может вызвать проблемы в коде. В будущих выпусках Kotlin мы введем поведение, при котором уровень видимости функции copy() по умолчанию будет совпадать с уровнем видимости конструктора. Это изменение будет внедряться постепенно, чтобы вы могли перенести код как можно более плавно.
План перехода начинается с Kotlin 2.0.20: в коде, где уровень видимости изменится в будущем, будут выдаваться предупреждения. Например:
// Triggers a warning in 2.0.20
data class PositiveInteger private constructor(val number: Int) {
companion object {
fun create(number: Int): PositiveInteger? = if (number > 0) PositiveInteger(number) else null
}
}
fun main() {
val positiveNumber = PositiveInteger.create(42) ?: return
// Triggers a warning in 2.0.20
val negativeNumber = positiveNumber.copy(number = -1)
// Warning: Non-public primary constructor is exposed via the generated 'copy()' method of the 'data' class.
// The generated 'copy()' will change its visibility in future releases.
}
Актуальная информация о плане перехода приведена в соответствующей задаче на YouTrack.
Чтобы предоставить вам больше контроля над этим поведением, в Kotlin 2.0.20 мы добавили две аннотации:
@ConsistentCopyVisibilityпозволяет включить новое поведение сейчас, прежде чем оно станет поведением по умолчанию в одном из следующих выпусков.@ExposedCopyVisibilityпозволяет отключить новое поведение и подавить предупреждения в месте объявления. Обратите внимание: даже с этой аннотацией компилятор по-прежнему выдает предупреждения при вызове функцииcopy().
Если вы хотите включить новое поведение в версии 2.0.20 для целого модуля, а не для отдельных классов, можно использовать параметр компилятора -Xconsistent-data-class-copy-visibility. Этот параметр действует так же, как добавление аннотации @ConsistentCopyVisibility ко всем классам данных модуля.
Поэтапная замена контекстных получателей контекстными параметрами
В Kotlin 1.6.20 мы представили контекстные получатели как экспериментальную функцию. Выслушав отзывы сообщества, мы решили не продолжать работу в этом направлении и выбрали другой путь.
В будущих выпусках Kotlin контекстные получатели будут заменены контекстными параметрами. Контекстные параметры пока находятся на стадии проектирования; предложение можно найти в KEEP.
Поскольку для реализации контекстных параметров требуются значительные изменения компилятора, мы решили не поддерживать одновременно контекстные получатели и контекстные параметры. Это решение значительно упрощает реализацию и снижает риск нестабильного поведения.
Мы понимаем, что контекстные получатели уже используются большим количеством разработчиков. Поэтому мы начнем постепенно прекращать их поддержку. План перехода начинается с Kotlin 2.0.20: при использовании контекстных получателей с параметром компилятора -Xcontext-receivers в коде будут выдаваться предупреждения. Например:
class MyContext
context(MyContext)
// Warning: Experimental context receivers are deprecated and will be superseded by context parameters.
// Please don't use context receivers. You can either pass parameters explicitly or use members with extensions.
fun someFunction() {
}
В будущих выпусках Kotlin это предупреждение станет ошибкой.
Если в коде используются контекстные получатели, рекомендуем перейти на один из следующих вариантов:
-
Явные параметры.
До
После
context(ContextReceiverType) fun someFunction() { contextReceiverMember() }fun someFunction(explicitContext: ContextReceiverType) { explicitContext.contextReceiverMember() } -
Функции-члены расширения (если возможно).
До
После
context(ContextReceiverType) fun contextReceiverMember() = TODO() context(ContextReceiverType) fun someFunction() { contextReceiverMember() }class ContextReceiverType { fun contextReceiverMember() = TODO() } fun ContextReceiverType.someFunction() { contextReceiverMember() }
Также можно дождаться выпуска Kotlin, в котором компилятор будет поддерживать контекстные параметры. Обратите внимание: изначально контекстные параметры будут представлены как экспериментальная функция.
Kotlin Multiplatform
В Kotlin 2.0.20 улучшено управление наборами исходного кода в мультиплатформенных проектах, а также объявлена устаревшей совместимость с некоторыми плагинами Gradle для Java в связи с недавними изменениями в Gradle.
Статические методы доступа к наборам исходного кода из иерархии целевых платформ по умолчанию
Начиная с Kotlin 1.9.20, шаблон иерархии по умолчанию автоматически применяется ко всем проектам Kotlin Multiplatform. Для всех наборов исходного кода из шаблона иерархии по умолчанию плагин Kotlin Gradle предоставлял безопасные по типу методы доступа. Благодаря этому наконец стало возможно обращаться к наборам исходного кода для всех указанных целевых платформ без использования конструкций by getting или by creating.
В Kotlin 2.0.20 работа с IDE стала еще удобнее. Теперь в блоке sourceSets {} предоставляются статические методы доступа для всех наборов исходного кода из шаблона иерархии по умолчанию. Мы считаем, что это изменение упростит обращение к наборам исходного кода по имени и сделает его более предсказуемым.
Теперь для каждого такого набора исходного кода доступен подробный комментарий KDoc с примером, а также диагностическое сообщение с предупреждением на случай, если вы попытаетесь обратиться к набору исходного кода, не объявив предварительно соответствующую целевую платформу:
kotlin {
jvm()
linuxX64()
linuxArm64()
mingwX64()
sourceSets {
commonMain.languageSettings {
progressiveMode = true
}
jvmMain { }
linuxX64Main { }
linuxArm64Main { }
// Warning: accessing source set without registering the target
iosX64Main { }
}
}

Подробнее о иерархической структуре проектов в Kotlin Multiplatform.
Устаревшая совместимость плагина Kotlin Multiplatform Gradle с плагинами Gradle для Java
В Kotlin 2.0.20 мы добавили предупреждение об устаревании, которое появляется при применении плагина Kotlin Multiplatform Gradle и любого из следующих плагинов Gradle для Java в одном проекте: Java, Java Library и Application. Предупреждение также появляется, если другой плагин Gradle в мультиплатформенном проекте применяет плагин Gradle для Java. Например, плагин Spring Boot Gradle автоматически применяет плагин Application.
Мы добавили это предупреждение об устаревании из-за принципиальных проблем совместимости между моделью проектов Kotlin Multiplatform и плагинами Java-экосистемы Gradle. Плагины Java-экосистемы Gradle в настоящее время не учитывают, что другие плагины могут:
Публиковать артефакты или выполнять компиляцию для целевой платформы JVM способом, отличным от плагинов Java-экосистемы.
Использовать в одном проекте две разные целевые платформы JVM, например JVM и Android.
Иметь сложную структуру мультиплатформенного проекта с потенциально несколькими целевыми платформами, отличными от JVM.
К сожалению, в настоящее время Gradle не предоставляет API для решения этих проблем.
Ранее в Kotlin Multiplatform мы использовали некоторые обходные решения для интеграции с плагинами Java-экосистемы. Однако они так и не устранили проблемы совместимости, а начиная с выпуска Gradle 8.8 использовать эти обходные решения больше невозможно. Дополнительную информацию см. в нашей задаче YouTrack.
Хотя мы пока не знаем, как именно решить эту проблему совместимости, мы намерены продолжать поддерживать компиляцию исходного кода Java в той или иной форме в ваших проектах Kotlin Multiplatform. Как минимум, мы будем поддерживать компиляцию исходного кода Java и использование плагина java-base из Gradle в мультиплатформенных проектах.
Тем временем, если вы увидели это предупреждение об устаревании в мультиплатформенном проекте, рекомендуем:
Определить, действительно ли вам нужен плагин Gradle для Java в проекте. Если нет, рассмотрите возможность его удаления.
Проверить, используется ли плагин Gradle для Java только для одной задачи. Если это так, возможно, вы сможете без особых усилий удалить плагин. Например, если задача использует плагин Gradle для Java для создания JAR-файла Javadoc, можно вместо этого определить задачу Javadoc вручную.
Если же вы хотите использовать в мультиплатформенном проекте и плагин Kotlin Multiplatform Gradle, и эти плагины Gradle для Java, рекомендуем:
Создать отдельный подпроект в мультиплатформенном проекте.
Применить плагин Gradle для Java в отдельном подпроекте.
Добавить в отдельный подпроект зависимость от родительского мультиплатформенного проекта.
Например, у вас есть мультиплатформенный проект с именем my-main-project, и вы хотите использовать плагин Gradle Application для запуска приложения JVM.
После создания подпроекта, назовем его subproject-A, структура родительского проекта должна выглядеть так:
.
├── build.gradle.kts
├── settings.gradle
├── subproject-A
└── build.gradle.kts
└── src
└── Main.java
В файле build.gradle.kts подпроекта примените плагин Application в блоке plugins {}:
plugins {
id("application")
}
plugins {
id('application')
}
В файле build.gradle.kts подпроекта добавьте зависимость от родительского мультиплатформенного проекта:
dependencies {
implementation(project(":my-main-project")) // The name of your parent multiplatform project
}
dependencies {
implementation project(':my-main-project') // The name of your parent multiplatform project
}
Теперь родительский проект настроен для работы с обоими плагинами.
Kotlin/Native
В Kotlin/Native улучшен сборщик мусора и добавлена возможность вызывать приостанавливающие функции Kotlin из Swift/Objective-C.
Конкурентная маркировка в сборщике мусора
В Kotlin 2.0.20 команда JetBrains делает еще один шаг к повышению производительности среды выполнения Kotlin/Native. Мы добавили экспериментальную поддержку конкурентной маркировки в сборщике мусора (GC).
По умолчанию потоки приложения приостанавливаются, пока GC маркирует объекты в куче. Это значительно влияет на длительность паузы GC, которая важна для производительности приложений, критичных к задержкам, например приложений с пользовательским интерфейсом, созданных с помощью Compose Multiplatform.
Теперь фаза маркировки при сборке мусора может выполняться одновременно с потоками приложения. Это должно значительно сократить время паузы GC и повысить отзывчивость приложения.
Как включить
В настоящее время эта функция является экспериментальной. Чтобы включить ее, задайте следующий параметр в файле gradle.properties:
kotlin.native.binary.gc=cms
Сообщайте о любых проблемах в наш баг-трекер YouTrack.
Поддержка встраивания bitcode удалена
Начиная с Kotlin 2.0.20, компилятор Kotlin/Native больше не поддерживает встраивание bitcode. Встраивание bitcode было объявлено устаревшим в Xcode 14 и удалено в Xcode 15 для всех целевых платформ Apple.
Теперь параметр embedBitcode для конфигурации фреймворка, а также аргументы командной строки -Xembed-bitcode и -Xembed-bitcode-marker объявлены устаревшими.
Если вы по-прежнему используете более ранние версии Xcode, но хотите перейти на Kotlin 2.0.20, отключите встраивание bitcode в проектах Xcode.
Изменения в мониторинге производительности GC с помощью signpost
В Kotlin 2.0.0 появилась возможность отслеживать производительность сборщика мусора (GC) Kotlin/Native с помощью Xcode Instruments. В Instruments есть инструмент signpost, который показывает паузы GC в виде событий. Это удобно при проверке зависаний, связанных с GC, в приложениях iOS.
Эта функция была включена по умолчанию, но, к сожалению, иногда приводила к сбоям при одновременном запуске приложения и Xcode Instruments. Начиная с Kotlin 2.0.20 для ее включения требуется явно указать следующий параметр компилятора:
-Xbinary=enableSafepointSignposts=true
Подробнее об анализе производительности GC см. в документации.
Вызов приостанавливающих функций Kotlin из Swift/Objective-C в потоках, отличных от главного
Ранее в Kotlin/Native по умолчанию действовало ограничение, согласно которому вызывать приостанавливающие функции Kotlin из Swift и Objective-C можно было только в главном потоке. В Kotlin 2.0.20 это ограничение снято: теперь вы можете запускать функции Kotlin suspend из Swift/Objective-C в любом потоке.
Если ранее вы переключили поведение по умолчанию для потоков, отличных от главного, с помощью бинарного параметра kotlin.native.binary.objcExportSuspendFunctionLaunchThreadRestriction=none, теперь его можно удалить из файла gradle.properties.
Kotlin/Wasm
В Kotlin 2.0.20 продолжается переход Kotlin/Wasm к именованным экспортам, а аннотация @ExperimentalWasmDsl перемещена.
Ошибка при использовании экспорта по умолчанию
В рамках перехода к именованным экспортам при использовании импорта по умолчанию для экспортов Kotlin/Wasm в JavaScript ранее выводилось предупреждение в консоль.
Для полноценной поддержки именованных экспортов это предупреждение теперь стало ошибкой. При использовании импорта по умолчанию вы увидите следующее сообщение об ошибке:
Do not use default import. Use the corresponding named import instead.
Это изменение является частью цикла устаревания, цель которого — перейти к именованным экспортам. Вот чего можно ожидать на каждом этапе:
В версии 2.0.0: в консоль выводится предупреждение о том, что экспорт сущностей через экспорты по умолчанию объявлен устаревшим.
В версии 2.0.20: возникает ошибка с требованием использовать соответствующий именованный импорт.
В версии 2.1.0: использование импортов по умолчанию полностью удаляется.
Новое расположение аннотации ExperimentalWasmDsl
Ранее аннотация @ExperimentalWasmDsl для функций WebAssembly (Wasm) располагалась в плагине Kotlin Gradle по следующему пути:
org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
В версии 2.0.20 аннотация @ExperimentalWasmDsl перемещена по адресу:
org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
Прежнее расположение теперь объявлено устаревшим и может привести к сбоям сборки из-за неразрешенных ссылок.
Чтобы учесть новое расположение аннотации @ExperimentalWasmDsl, обновите оператор импорта в сценариях сборки Gradle. Используйте явный импорт нового расположения @ExperimentalWasmDsl:
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
Также можно удалить оператор импорта со звездочкой из старого пакета:
import org.jetbrains.kotlin.gradle.targets.js.dsl.*
Kotlin/JS
В Kotlin/JS представлены экспериментальные функции для поддержки статических членов в JavaScript и создания коллекций Kotlin из JavaScript.
Поддержка использования статических членов Kotlin в JavaScript
Начиная с Kotlin 2.0.20, можно использовать аннотацию @JsStatic. Она работает подобно @JvmStatic и указывает компилятору сгенерировать дополнительные статические методы для целевого объявления. Это позволяет использовать статические члены непосредственно в JavaScript-коде Kotlin.
Аннотацию @JsStatic можно использовать для функций, определенных в именованных объектах, а также в сопутствующих объектах, объявленных внутри классов и интерфейсов. Компилятор генерирует статический метод объекта и метод экземпляра в самом объекте. Например:
class C {
companion object {
@JsStatic
fun callStatic() {}
fun callNonStatic() {}
}
}
Теперь callStatic() является статическим в JavaScript, а callNonStatic() — нет:
C.callStatic(); // Works, accessing the static function C.callNonStatic(); // Error, not a static function in the generated JavaScript C.Companion.callStatic(); // Instance method remains C.Companion.callNonStatic(); // The only way it works
Аннотацию @JsStatic также можно применить к свойству объекта или сопутствующего объекта, сделав методы чтения и записи статическими членами этого объекта или класса, содержащего сопутствующий объект.
Возможность создавать коллекции Kotlin из JavaScript
В Kotlin 2.0.0 появилась возможность экспортировать коллекции Kotlin в JavaScript (и TypeScript). Теперь команда JetBrains делает еще один шаг к улучшению взаимодействия коллекций. Начиная с Kotlin 2.0.20, коллекции Kotlin можно создавать непосредственно со стороны JavaScript/TypeScript.
Вы можете создавать коллекции Kotlin в JavaScript и передавать их в качестве аргументов экспортируемым конструкторам или функциям. Как только коллекция упоминается в экспортируемом объявлении, Kotlin генерирует для нее фабрику, доступную в JavaScript/TypeScript.
Рассмотрим следующую экспортируемую функцию:
// Kotlin @JsExport fun consumeMutableMap(map: MutableMap<String, Int>)
Поскольку упоминается коллекция MutableMap, Kotlin генерирует объект с фабричным методом, доступным из JavaScript/TypeScript. Затем этот фабричный метод создает MutableMap из JavaScript Map:
// JavaScript
import { consumeMutableMap } from "an-awesome-kotlin-module"
import { KtMutableMap } from "an-awesome-kotlin-module/kotlin-kotlin-stdlib"
consumeMutableMap(
KtMutableMap.fromJsMap(new Map([["First", 1], ["Second", 2]]))
)
Эта функция доступна для типов коллекций Kotlin Set, Map и List, а также для их изменяемых вариантов.
Gradle
Kotlin 2.0.20 полностью совместим с Gradle 6.8.3–8.6. Также поддерживаются Gradle 8.7 и 8.8, за одним исключением: если вы используете плагин Kotlin Multiplatform Gradle, в многоплатформенных проектах могут появиться предупреждения об устаревании при вызове функции withJava() для целевой платформы JVM. Мы планируем исправить эту проблему как можно скорее.
Дополнительную информацию см. в задаче в YouTrack.
Вы также можете использовать версии Gradle вплоть до последнего выпуска, но имейте в виду, что в этом случае вы можете столкнуться с предупреждениями об устаревании или некоторые новые функции Gradle могут не работать.
В этой версии появились такие изменения, как начало процесса отказа от старого подхода к инкрементальной компиляции на основе файлов истории JVM, а также новый способ обмена артефактами JVM между проектами.
Устаревший подход к инкрементальной компиляции на основе файлов истории JVM
В Kotlin 2.0.20 подход к инкрементальной компиляции на основе файлов истории JVM объявлен устаревшим в пользу нового подхода к инкрементальной компиляции, включенного по умолчанию начиная с Kotlin 1.8.20.
Подход к инкрементальной компиляции на основе файлов истории JVM имел ряд ограничений: например, он не работал с кэшем сборки Gradle и не поддерживал избегание компиляции. Новый подход к инкрементальной компиляции, напротив, лишен этих ограничений и с момента появления показывает хорошие результаты.
Поскольку новый подход к инкрементальной компиляции использовался по умолчанию в двух последних крупных выпусках Kotlin, свойство Gradle kotlin.incremental.useClasspathSnapshot объявлено устаревшим в Kotlin 2.0.20. Поэтому при его использовании для отказа от нового подхода вы увидите предупреждение об устаревании.
Возможность обмениваться артефактами JVM между проектами в виде файлов классов
В Kotlin 2.0.20 мы представляем новый подход, меняющий способ обмена результатами компиляции Kotlin/JVM, например файлами JAR, между проектами. При таком подходе конфигурация Gradle apiElements теперь имеет вторичный вариант, предоставляющий доступ к каталогу с скомпилированными файлами .class. При такой настройке проект использует этот каталог, а не запрашивает сжатый артефакт JAR во время компиляции. Это уменьшает количество операций сжатия и распаковки файлов JAR, особенно при инкрементальных сборках.
Наши тесты показывают, что этот новый подход может повысить производительность сборки в Linux и macOS. Однако на компьютерах с Windows мы наблюдали снижение производительности из-за особенностей обработки операций ввода-вывода при работе с файлами.
Чтобы попробовать этот новый подход, добавьте следующее свойство в файл gradle.properties:
kotlin.jvm.addClassesVariant=true
По умолчанию для этого свойства задано значение false, и вариант apiElements в Gradle запрашивает сжатый артефакт JAR.
Будем благодарны за ваши отзывы об этом новом подходе. Заметили ли вы повышение производительности при его использовании? Оставьте комментарий в YouTrack.
Согласованное поведение зависимостей плагина Kotlin Gradle с плагином java-test-fixtures
До Kotlin 2.0.20 при использовании в проекте плагина java-test-fixtures поведение Gradle и плагина Kotlin Gradle различалось в части передачи зависимостей.
Плагин Kotlin Gradle передавал зависимости:
из типов зависимостей
implementationиapiплагинаjava-test-fixturesв путь к классам компиляции набора исходного кодаtest.из типов зависимостей
implementationиapiосновного набора исходного кода в путь к классам компиляции набора исходного кода плагинаjava-test-fixtures.
Однако Gradle передавал зависимости только для типа зависимости api.
Из-за этого различия в поведении некоторые проекты находили файлы ресурсов в пути к классам несколько раз.
Начиная с Kotlin 2.0.20 поведение плагина Kotlin Gradle согласовано с поведением плагина Gradle java-test-fixtures, поэтому эта проблема больше не возникает с этим и другими плагинами Gradle.
В результате этого изменения некоторые зависимости в наборах исходного кода test и testFixtures могут стать недоступными. В этом случае измените тип объявления зависимости с implementation на api или добавьте новое объявление зависимости для затронутого набора исходного кода.
Добавлена зависимость задачи для редких случаев, когда у задачи компиляции отсутствует зависимость от артефакта
До версии 2.0.20 мы обнаружили сценарии, в которых у задачи компиляции отсутствовала зависимость от задачи, создающей один из входных артефактов. В результате задача компиляции зависела от нестабильного результата: иногда артефакт успевал создаться, а иногда — нет.
Чтобы исправить эту проблему, плагин Kotlin Gradle теперь автоматически добавляет необходимую зависимость от задачи в таких сценариях.
В очень редких случаях новое поведение может привести к ошибке циклической зависимости. Например, если у вас есть несколько компиляций, одна из которых видит все внутренние объявления другой, а создаваемый артефакт зависит от результатов обеих задач компиляции, вы можете увидеть ошибку следующего вида:
FAILURE: Build failed with an exception.
What went wrong:
Circular dependency between the following tasks:
:lib:compileKotlinJvm
--- :lib:jvmJar
\--- :lib:compileKotlinJvm (*)
(*) - details omitted (listed previously)
Чтобы исправить эту ошибку циклической зависимости, мы добавили свойство Gradle: archivesTaskOutputAsFriendModule.
По умолчанию для этого свойства задано значение true, чтобы отслеживать зависимость от задачи. Чтобы отключить использование артефакта в задаче компиляции и тем самым убрать необходимость в зависимости от задачи, добавьте следующее в файл gradle.properties:
kotlin.build.archivesTaskOutputAsFriendModule=false
Дополнительную информацию см. в задаче в YouTrack.
Компилятор Compose
В Kotlin 2.0.20 компилятор Compose получил несколько улучшений.
Исправлена проблема с ненужными рекомпозициями, появившаяся в версии 2.0.0
В компиляторе Compose 2.0.0 есть проблема: в многоплатформенных проектах с целевыми платформами, отличными от JVM, он иногда неверно определяет стабильность типов. Это может приводить к ненужным (или даже бесконечным) рекомпозициям. Мы настоятельно рекомендуем обновить приложения Compose, созданные с Kotlin 2.0.0, до версии 2.0.10 или новее.
Если ваше приложение собрано с компилятором Compose 2.0.10 или новее, но использует зависимости, собранные с версией 2.0.0, эти старые зависимости по-прежнему могут вызывать проблемы с рекомпозицией. Чтобы избежать этого, обновите зависимости до версий, собранных с тем же компилятором Compose, что и приложение.
Новый способ настройки параметров компилятора
Мы добавили новый механизм настройки параметров, чтобы избежать постоянных изменений параметров верхнего уровня. Команде компилятора Compose сложнее тестировать изменения, добавляя или удаляя записи верхнего уровня в блоке composeCompiler {}. Поэтому такие параметры, как режим усиленного пропуска и оптимизации групп без пропуска, теперь включаются с помощью свойства featureFlags. Это свойство будет использоваться для тестирования новых параметров компилятора Compose, которые впоследствии станут значениями по умолчанию.
Это изменение также применено к плагину Compose compiler Gradle. В дальнейшем для настройки флагов функций используйте следующий синтаксис (этот код переключит все значения по умолчанию):
composeCompiler {
featureFlags = setOf(
ComposeFeatureFlag.IntrinsicRemember.disabled(),
ComposeFeatureFlag.OptimizeNonSkippingGroups,
ComposeFeatureFlag.StrongSkipping.disabled()
)
}
Если же вы настраиваете компилятор Compose напрямую, используйте следующий синтаксис:
-P plugin:androidx.compose.compiler.plugins.kotlin:featureFlag=IntrinsicRemember
Свойства enableIntrinsicRemember, enableNonSkippingGroupOptimization и enableStrongSkippingMode объявлены устаревшими.
Будем благодарны за ваши отзывы об этом новом подходе в YouTrack.
Режим усиленного пропуска включен по умолчанию
Режим усиленного пропуска для компилятора Compose теперь включен по умолчанию.
Режим усиленного пропуска — это параметр компилятора Compose, изменяющий правила пропуска компонуемых функций. Если этот режим включен, можно пропускать и компонуемые функции с нестабильными параметрами. Кроме того, режим усиленного пропуска автоматически запоминает лямбда-выражения, используемые в компонуемых функциях, поэтому для предотвращения рекомпозиции больше не нужно оборачивать лямбда-выражения в remember.
Подробнее см. в документации по режиму усиленного пропуска.
Маркеры трассировки композиции включены по умолчанию
В плагине Compose compiler Gradle для параметра includeTraceMarkers теперь по умолчанию задано значение true, соответствующее значению по умолчанию в плагине компилятора. Это позволяет просматривать компонуемые функции в профилировщике системной трассировки Android Studio. Подробности о трассировке композиции см. в публикации в блоге Android Developers.
Оптимизации групп без пропуска
В этом выпуске появился новый параметр компилятора: при его включении для компонуемых функций, которые нельзя пропустить и перезапустить, больше не будет создаваться группа вокруг тела функции. Это сокращает количество выделений памяти и повышает производительность. Этот параметр экспериментальный и по умолчанию отключен, но его можно включить с помощью флага функции OptimizeNonSkippingGroups, как показано выше.
Этот флаг функции теперь готов к более широкому тестированию. Обо всех проблемах, обнаруженных при его включении, можно сообщить в системе отслеживания ошибок Google.
Поддержка параметров по умолчанию в абстрактных компонуемых функциях
Теперь можно добавлять параметры по умолчанию в абстрактные компонуемые функции.
Ранее компилятор Compose выдавал ошибку при попытке сделать это, хотя такой код допустим в Kotlin. Теперь компилятор Compose поддерживает эту возможность, и ограничение снято. Это особенно полезно для добавления значений Modifier по умолчанию:
abstract class Composables {
@Composable
abstract fun Composable(modifier: Modifier = Modifier)
}
В версии 2.0.20 для открытых компонуемых функций по-прежнему действуют ограничения на параметры по умолчанию. Это ограничение будет устранено в будущих выпусках.
Стандартная библиотека
Стандартная библиотека теперь поддерживает универсальные уникальные идентификаторы в качестве экспериментальной функции и включает некоторые изменения в декодировании Base64.
Поддержка UUID в общей стандартной библиотеке Kotlin
В Kotlin 2.0.20 в общую стандартную библиотеку Kotlin добавлен класс для представления UUID (универсальных уникальных идентификаторов), предназначенный для решения задачи уникальной идентификации объектов.
Кроме того, эта функция предоставляет API для следующих операций с UUID:
Создание UUID.
Разбор UUID из строкового представления и форматирование UUID в строковое представление.
Создание UUID из заданных 128-битных значений.
Получение 128 бит UUID.
Следующий пример кода демонстрирует эти операции:
// Constructs a byte array for UUID creation
val byteArray = byteArrayOf(
0x55, 0x0E, 0x84.toByte(), 0x00, 0xE2.toByte(), 0x9B.toByte(), 0x41, 0xD4.toByte(),
0xA7.toByte(), 0x16, 0x44, 0x66, 0x55, 0x44, 0x00, 0x00
)
val uuid1 = Uuid.fromByteArray(byteArray)
val uuid2 = Uuid.fromULongs(0x550E8400E29B41D4uL, 0xA716446655440000uL)
val uuid3 = Uuid.parse("550e8400-e29b-41d4-a716-446655440000")
println(uuid1)
// 550e8400-e29b-41d4-a716-446655440000
println(uuid1 == uuid2)
// true
println(uuid2 == uuid3)
// true
// Accesses UUID bits
val version = uuid1.toLongs { mostSignificantBits, _ ->
((mostSignificantBits shr 12) and 0xF).toInt()
}
println(version)
// 4
// Generates a random UUID
val randomUuid = Uuid.random()
println(uuid1 == randomUuid)
// false
Для совместимости с API, использующими java.util.UUID, в Kotlin/JVM предусмотрены две функции-расширения для преобразования между java.util.UUID и kotlin.uuid.Uuid: .toJavaUuid() и .toKotlinUuid(). Например:
val kotlinUuid = Uuid.parseHex("550e8400e29b41d4a716446655440000")
// Converts Kotlin UUID to java.util.UUID
val javaUuid = kotlinUuid.toJavaUuid()
val javaUuid = java.util.UUID.fromString("550e8400-e29b-41d4-a716-446655440000")
// Converts Java UUID to kotlin.uuid.Uuid
val kotlinUuid = javaUuid.toKotlinUuid()
Эта функция и предоставляемые API упрощают разработку многоплатформенного программного обеспечения, позволяя совместно использовать код на нескольких платформах. UUID также идеально подходят для сред, в которых сложно создавать уникальные идентификаторы.
Примеры использования UUID:
Назначение уникальных идентификаторов записям базы данных.
Создание идентификаторов веб-сеансов.
Любые сценарии, в которых требуется уникальная идентификация или отслеживание.
Поддержка minLength в HexFormat
В Kotlin 2.0.20 в класс NumberHexFormat добавлено новое свойство minLength, доступное через HexFormat.number. Это свойство позволяет задать минимальное количество цифр в шестнадцатеричном представлении числовых значений, добавляя нули в начало, чтобы обеспечить нужную длину. Кроме того, начальные нули можно удалить с помощью свойства removeLeadingZeros:
fun main() {
println(93.toHexString(HexFormat {
number.minLength = 4
number.removeLeadingZeros = true
}))
// "005d"
}
Свойство minLength не влияет на разбор. Однако теперь при разборе допускаются шестнадцатеричные строки, содержащие больше цифр, чем разрядность типа, если лишние начальные цифры — нули.
Изменения в поведении декодера Base64
В Kotlin 2.0.20 в поведение декодера Base64 внесены два изменения:
Для настройки заполнения добавлена функция
withPadding
Теперь декодер Base64 требует заполнения
Теперь кодировщик Base64 по умолчанию добавляет символы заполнения, а декодер требует их наличия и запрещает ненулевые биты заполнения при декодировании.
Функция withPadding для настройки заполнения
Добавлена новая функция .withPadding(), позволяющая управлять поведением заполнения при кодировании и декодировании Base64:
val base64 = Base64.UrlSafe.withPadding(Base64.PaddingOption.ABSENT_OPTIONAL)
Эта функция позволяет создавать экземпляры Base64 с различными параметрами заполнения:
|
При кодировании |
При декодировании |
|---|---|---|
|
Добавляется заполнение |
Заполнение обязательно |
|
Заполнение не добавляется |
Заполнение не допускается |
|
Добавляется заполнение |
Заполнение необязательно |
|
Заполнение не добавляется |
Заполнение необязательно |
Можно создавать экземпляры Base64 с различными параметрами заполнения и использовать их для кодирования и декодирования данных:
import kotlin.io.encoding.Base64
import kotlin.io.encoding.ExperimentalEncodingApi
@OptIn(ExperimentalEncodingApi::class)
fun main() {
// Example data to encode
val data = "fooba".toByteArray()
// Creates a Base64 instance with URL-safe alphabet and PRESENT padding
val base64Present = Base64.UrlSafe.withPadding(Base64.PaddingOption.PRESENT)
val encodedDataPresent = base64Present.encode(data)
println("Encoded data with PRESENT padding: $encodedDataPresent")
// Encoded data with PRESENT padding: Zm9vYmE=
// Creates a Base64 instance with URL-safe alphabet and ABSENT padding
val base64Absent = Base64.UrlSafe.withPadding(Base64.PaddingOption.ABSENT)
val encodedDataAbsent = base64Absent.encode(data)
println("Encoded data with ABSENT padding: $encodedDataAbsent")
// Encoded data with ABSENT padding: Zm9vYmE
// Decodes the data back
val decodedDataPresent = base64Present.decode(encodedDataPresent)
println("Decoded data with PRESENT padding: ${String(decodedDataPresent)}")
// Decoded data with PRESENT padding: fooba
val decodedDataAbsent = base64Absent.decode(encodedDataAbsent)
println("Decoded data with ABSENT padding: ${String(decodedDataAbsent)}")
// Decoded data with ABSENT padding: fooba
}
Обновления документации
В документацию Kotlin внесены заметные изменения:
Улучшена страница о стандартном вводе — узнайте, как использовать Java Scanner и
readln().Улучшено руководство по миграции на компилятор K2 — узнайте об улучшении производительности, совместимости с библиотеками Kotlin и о том, что делать с пользовательскими плагинами компилятора.
Улучшена страница об исключениях — узнайте об исключениях, а также о том, как их выбрасывать и перехватывать.
Улучшено руководство по написанию тестового кода для JVM с использованием JUnit — узнайте, как создавать тесты с помощью JUnit.
Улучшена страница о взаимодействии со Swift/Objective-C — узнайте, как использовать объявления Kotlin в коде Swift/Objective-C и объявления Objective-C в коде Kotlin.
Улучшена страница о настройке экспорта пакетов Swift — узнайте, как настроить вывод Kotlin/Native, который можно использовать в качестве зависимости менеджера пакетов Swift.
Установка Kotlin 2.0.20
Начиная с IntelliJ IDEA 2023.3 и Android Studio Iguana (2023.2.1) Canary 15 плагин Kotlin распространяется как встроенный плагин, включенный в состав IDE. Это означает, что теперь установить плагин из JetBrains Marketplace нельзя.
Чтобы перейти на новую версию Kotlin, измените версию Kotlin в сценариях сборки на 2.0.20.
© 2010–2026 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/whatsnew2020.html