Что нового в Kotlin 1.8.20
Вышел релиз Kotlin 1.8.20, и вот некоторые из его ключевых моментов:
Предварительный просмотр поддержки Gradle составных билдов в Kotlin Multiplatform
Экспериментальная поддержка интерфейса AutoCloseable в стандартной библиотеке
Экспериментальная поддержка кодирования Base64 в стандартной библиотеке
Также вы можете найти краткий обзор изменений в этом видео:
Поддержка IDE
Плагины Kotlin, поддерживающие 1.8.20, доступны для:
IDE |
Поддерживаемые версии |
|---|---|
IntelliJ IDEA |
2022.2.x, 2022.3.x, 2023.1.x |
Android Studio |
Flamingo (222) |
Новые обновления компилятора Kotlin K2
Команда Kotlin продолжает стабилизировать компилятор K2. Как упоминалось в объявлении о Kotlin 1.7.0, он все еще находится в стадии альфа-версии. Этот релиз включает в себя дальнейшие улучшения на пути к бета-версии K2.
Начиная с этого релиза 1.8.20, компилятор Kotlin K2:
Имеет предварительную версию плагина сериализации.
Предоставляет поддержку в стадии альфа-версии для компилятора JS IR.
Предвосхищает будущий релиз новой версии языка, Kotlin 2.0.
Узнайте больше о новом компиляторе и его преимуществах в следующих видео:
Как включить компилятор Kotlin K2
Для включения и тестирования компилятора Kotlin K2 используйте новую версию языка с следующим параметром компилятора:
-language-version 2.0
Вы можете указать его в вашем build.gradle(.kts) файле:
kotlin {
sourceSets.all {
languageSettings {
languageVersion = "2.0"
}
}
}
Предыдущий параметр компилятора -Xuse-k2 был устаревшим.
Оставьте свои отзывы о новом компиляторе K2
Мы будем рады получить любые ваши отзывы!
Направляйте свои отзывы непосредственно разработчикам K2 на Kotlin Slack – получите приглашение и присоединяйтесь к каналу #k2-early-adopters.
Сообщайте о любых проблемах, с которыми вы столкнулись при использовании нового компилятора K2, на нашей системе отслеживания ошибок.
Включите опцию отправки статистических данных использования, чтобы позволить JetBrains собирать анонимные данные об использовании K2.
Язык
По мере дальнейшего развития Kotlin мы вводим предварительные версии новых функций языка в 1.8.20:
Современная и производительная замена функции Enum class values
Отмена ограничений на вторичные конструкторы с телами в классах inline
Современная и производительная замена функции Enum class values
Классы перечислений имеют синтетическую values() функцию, которая возвращает массив определенных констант перечисления. Однако использование массива может привести к скрытым проблемам производительности в Kotlin и Java. Кроме того, большинство API используют коллекции, что требует последующего преобразования. Для решения этих проблем мы ввели свойство entries для классов перечисления, которое следует использовать вместо функции values(). При вызове свойство entries возвращает предварительно выделенный неизменяемый список определенных констант перечисления.
enum class Color(val colorName: String, val rgb: String) {
RED("Red", "#FF0000"),
ORANGE("Orange", "#FF7F00"),
YELLOW("Yellow", "#FFFF00")
}
@OptIn(ExperimentalStdlibApi::class)
fun findByRgb(rgb: String): Color? = Color.entries.find { it.rgb == rgb }
Как включить свойство entries
Чтобы попробовать эту функцию, включите @OptIn(ExperimentalStdlibApi) и активируйте компиляторную опцию -language-version 1.9. В проекте Gradle это можно сделать, добавив следующее в ваш build.gradle(.kts):
tasks
.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask<*>>()
.configureEach {
compilerOptions
.languageVersion
.set(
org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9
)
}
tasks
.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask.class)
.configureEach {
compilerOptions.languageVersion =
org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9
}
Дополнительную информацию о предложении см. в заметке KEEP.
Предварительный просмотр объектов данных для симметрии с классами данных
Объекты данных позволяют объявлять объекты с семантикой синглтона и чистым toString() представлением. В этом фрагменте вы можете увидеть, как добавление ключевого слова data к объявлению объекта улучшает читаемость его toString() вывода:
package org.example
object MyObject
data object MyDataObject
fun main() {
println(MyObject) // org.example.MyObject@1f32e575
println(MyDataObject) // MyDataObject
}
В особенности для sealed иерархий (например, sealed class или sealed interface иерархии), data objects идеально подходят, потому что они удобно используются вместе с 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.7.20 семантика объектов данных была уточнена. Компилятор теперь автоматически генерирует ряд удобных функций для них:
toString
Функция toString() объекта данных возвращает простое имя объекта:
data object MyDataObject {
val x: Int = 3
}
fun main() {
println(MyDataObject) // MyDataObject
}
equals и hashCode
Функция equals() для data object гарантирует, что все объекты, имеющие тип вашего data object, считаются равными. В большинстве случаев у вас будет только один экземпляр вашего объекта данных во время выполнения (поскольку data object объявляет синглтон). Однако в крайнем случае, когда другой объект того же типа генерируется во время выполнения (например, через платформенное отражение через java.lang.reflect, или с помощью библиотеки сериализации JVM, которая использует этот API в фоновом режиме), это гарантирует, что объекты будут считаться равными.
Убедитесь, что сравниваете data objects структурно (используя оператор ==) и никогда по ссылке (оператор ===). Это помогает избежать ловушек, когда существует более одного экземпляра объекта данных во время выполнения. Следующий фрагмент иллюстрирует этот конкретный случай:
import java.lang.reflect.Constructor
data object MySingleton
fun main() {
val evilTwin = createInstanceViaReflection()
println(MySingleton) // MySingleton
println(evilTwin) // MySingleton
// Even when a library forcefully creates a second instance of MySingleton, its `equals` method returns true:
println(MySingleton == evilTwin) // true
// Do not compare data objects via ===.
println(MySingleton === evilTwin) // false
}
fun createInstanceViaReflection(): MySingleton {
// Kotlin reflection does not permit the instantiation of data objects.
// This creates a new MySingleton instance "by force" (i.e., Java platform reflection)
// Don't do this yourself!
return (MySingleton.javaClass.declaredConstructors[0].apply { isAccessible = true } as Constructor<MySingleton>).newInstance()
}
Поведение сгенерированной функции hashCode() согласуется с поведением функции equals(), так что все экземпляры во время выполнения data object имеют одинаковый хэш-код.
Отсутствие функций copy и componentN для объектов данных
Хотя data object и data class объявления часто используются вместе и имеют некоторые сходства, для data object не генерируются некоторые функции:
Поскольку объявление data object предназначено для использования в качестве объекта-синглтона, не генерируется никакая функция copy(). Паттерн синглтона ограничивает создание класса одним экземпляром, а разрешение копировать экземпляр нарушит это ограничение.
Также в отличие от data class, data object не имеет свойств данных. Поскольку попытка разобрать такой объект не имеет смысла, не генерируются никакие функции componentN().
Мы будем рады вашим отзывам об этой функции в YouTrack.
Как включить предварительный просмотр объектов данных
Чтобы попробовать эту функцию, включите опцию компилятора -language-version 1.9. В проекте Gradle это можно сделать, добавив следующее в ваш build.gradle(.kts):
tasks
.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask<*>>()
.configureEach {
compilerOptions
.languageVersion
.set(
org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9
)
}
tasks
.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask.class)
.configureEach {
compilerOptions.languageVersion =
org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9
}
Предварительный просмотр отмены ограничений на вторичные конструкторы с телами в классах inline
Kotlin 1.8.20 снимает ограничения на использование вторичных конструкторов с телами в классах inline.
Классы inline раньше допускали только публичный первичный конструктор без init блоков или вторичных конструкторов для ясной семантики инициализации. В результате невозможно было инкапсулировать базовые значения или создать класс inline, который бы представлял некоторые ограниченные значения.
Эти проблемы были решены, когда Kotlin 1.4.30 снял ограничения на init блоки. Теперь мы делаем ещё один шаг вперёд и разрешаем вторичные конструкторы с телами в режиме предварительного просмотра:
@JvmInline
value class Person(private val fullName: String) {
// Allowed since Kotlin 1.4.30:
init {
check(fullName.isNotBlank()) {
"Full name shouldn't be empty"
}
}
// Preview available since Kotlin 1.8.20:
constructor(name: String, lastName: String) : this("$name $lastName") {
check(lastName.isNotBlank()) {
"Last name shouldn't be empty"
}
}
}
Как включить вторичные конструкторы с телами
Чтобы попробовать эту функцию, включите опцию компилятора -language-version 1.9. В проекте Gradle это можно сделать, добавив следующее в ваш build.gradle(.kts):
tasks
.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask<*>>()
.configureEach {
compilerOptions
.languageVersion
.set(
org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9
)
}
tasks
.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask.class)
.configureEach {
compilerOptions.languageVersion =
org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9
}
Мы рекомендуем вам попробовать эту функцию и отправить все отчеты на YouTrack YouTrack, чтобы помочь нам сделать её стандартной в Kotlin 1.9.0.
Подробнее об разработке Kotlin inline classes в этом KEEP.
Новый целевой набор Kotlin/Wasm
Kotlin/Wasm (Kotlin WebAssembly) в этом предварительном выпуске имеет статус Экспериментальный. Команда Kotlin считает WebAssembly перспективной технологией и стремится найти лучшие способы ее использования, извлекая все преимущества Kotlin.
Формат бинарного кода WebAssembly независим от платформы, поскольку он выполняется с помощью собственной виртуальной машины. Почти все современные браузеры уже поддерживают WebAssembly 1.0. Для настройки среды выполнения WebAssembly необходимо только включить экспериментальный режим сборки мусора, на который нацелен Kotlin/Wasm. Подробные инструкции вы найдете здесь: Как включить Kotlin/Wasm.
Мы хотим выделить следующие преимущества нового целевого набора Kotlin/Wasm:
Более высокая скорость компиляции по сравнению с целевым набором
wasm32Kotlin/Native, поскольку Kotlin/Wasm не использует LLVM.Более простая взаимосвязь с JS и интеграция с браузерами по сравнению с целевым набором
wasm32, благодаря сборке мусора Wasm.Потенциально более быстрый запуск приложения по сравнению с Kotlin/JS и JavaScript, поскольку Wasm имеет компактный и легко интерпретируемый байткод.
Повышенная производительность приложения во время выполнения по сравнению с Kotlin/JS и JavaScript, поскольку Wasm — статически типизированный язык.
Начиная с предварительного выпуска 1.8.20-RC2, вы можете использовать Kotlin/Wasm в своих экспериментальных проектах. Мы предоставляем стандартную библиотеку Kotlin (stdlib) и библиотеку тестов (kotlin.test) для Kotlin/Wasm «из коробки». Поддержка IDE будет добавлена в будущих выпусках.
Узнайте больше о Kotlin/Wasm в этом видео на YouTube.
Как включить Kotlin/Wasm
Для включения и тестирования Kotlin/Wasm обновите свой build.gradle.kts файл:
plugins {
kotlin("multiplatform") version "1.8.20-RC2"
}
kotlin {
wasm {
binaries.executable()
browser {
}
}
sourceSets {
val commonMain by getting
val commonTest by getting {
dependencies {
implementation(kotlin("test"))
}
}
val wasmMain by getting
val wasmTest by getting
}
}
Для запуска проекта Kotlin/Wasm необходимо обновить настройки целевой среды:
-
Для версии 109:
Запустите приложение со строковым аргументом командной строки
--js-flags=--experimental-wasm-gc. -
Для версии 110 или выше:
Перейдите к
chrome://flags/#enable-webassembly-garbage-collectionв вашем браузере.Включите Сборку мусора WebAssembly.
Перезапустите ваш браузер.
Для версии 109 или выше:
Перейдите к
about:configв вашем браузере.Включите опции
javascript.options.wasm_function_referencesиjavascript.options.wasm_gc.Перезапустите ваш браузер.
Для версии 109 или выше:
Запустите приложение со строковым аргументом командной строки --js-flags=--experimental-wasm-gc.
Оставьте свой отзыв о Kotlin/Wasm
Мы будем признательны за любой ваш отзыв!
Напишите разработчикам в Kotlin Slack – получите приглашение и присоединитесь к каналу #webassembly.
Сообщите о любых проблемах, с которыми вы столкнулись при использовании Kotlin/Wasm, на этой странице YouTrack.
Kotlin/JVM
Kotlin 1.8.20 включает предварительный просмотр Java синтетических ссылок на свойства и поддержку JVM IR-бекенда в задаче генерации псевдокода kapt по умолчанию.
Предварительный просмотр Java синтетических ссылок на свойства
Kotlin 1.8.20 добавляет возможность создания ссылок на Java синтетические свойства, например, для такого Java кода:
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
Kotlin всегда позволял вам писать person.age, где age — это синтетическое свойство. Теперь вы также можете создавать ссылки на Person::age и person::age. Всё то же самое работает и для name.
val persons = listOf(Person("Jack", 11), Person("Sofie", 12), Person("Peter", 11))
Persons
// Call a reference to Java synthetic property:
.sortedBy(Person::age)
// Call Java getter via the Kotlin property syntax:
.forEach { person -> println(person.name) }
}
Как включить Java синтетические ссылки на свойства
Чтобы попробовать эту функцию, включите параметр компилятора -language-version 1.9. В проекте Gradle вы можете сделать это, добавив следующее в ваш build.gradle(.kts).
tasks
.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask<*>>()
.configureEach {
compilerOptions
.languageVersion
.set(
org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9
)
}
tasks
.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask.class)
.configureEach {
compilerOptions.languageVersion =
org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9
}
Поддержка JVM IR-бекенда в задаче генерации псевдокода kapt по умолчанию
В Kotlin 1.7.20 мы добавили поддержку JVM IR-бекенда в задаче генерации псевдокода kapt. Начиная с этого выпуска, эта поддержка работает по умолчанию. Вам больше не нужно указывать kapt.use.jvm.ir=true в вашем gradle.properties для её включения. Мы будем благодарны за ваш отзыв об этой функции на YouTrack.
Kotlin/Native
Kotlin 1.8.20 включает изменения в поддерживаемых целях Kotlin/Native, межплатформенной совместимости с Objective-C и улучшения плагина CocoaPods Gradle, среди других обновлений:
Обновление для целей Kotlin/Native
Команда Kotlin решила пересмотреть список целей, поддерживаемых Kotlin/Native, разделить их на уровни и устареть некоторые из них, начиная с Kotlin 1.8.20. См. раздел Поддержка целей Kotlin/Native для полного списка поддерживаемых и устаревших целей.
Следующие цели были устаревшими с Kotlin 1.8.20 и будут удалены в версии 1.9.20:
iosArm32watchosX86wasm32mingwX86linuxArm32HfplinuxMips32linuxMipsel32
Что касается оставшихся целей, теперь есть три уровня поддержки в зависимости от того, насколько хорошо цель поддерживается и тестируется в компиляторе Kotlin/Native. Цель может быть перемещена в другой уровень. Например, мы постараемся предоставить полную поддержку iosArm64 в будущем, поскольку это важно для Kotlin Multiplatform Mobile.
Если вы автор библиотеки, эти уровни поддержки могут помочь вам определить, на каких целях следует тестировать инструменты непрерывной интеграции и какие можно пропустить. Команда Kotlin будет использовать тот же подход при разработке официальных библиотек Kotlin, таких как kotlinx.coroutines.
Посмотрите наш блог-пост, чтобы узнать больше о причинах этих изменений.
Устаревание устаревшего менеджера памяти
Начиная с версии 1.8.20, устаревший менеджер памяти будет удален в версии 1.9.20. Новый менеджер памяти был включён по умолчанию в версии 1.7.20 и с тех пор получал дальнейшие обновления стабильности и улучшения производительности.
Если вы всё ещё используете устаревший менеджер памяти, удалите опцию kotlin.native.binary.memoryModel=strict из вашего gradle.properties и следуйте нашей Руководству по миграции, чтобы внести необходимые изменения.
Новый менеджер памяти не поддерживает цель wasm32. Эта цель также устарела начиная с этого выпуска и будет удалена в версии 1.9.20.
Поддержка заголовков Objective-C с директивами @import
Kotlin/Native теперь может импортировать заголовки Objective-C с директивами @import. Эта функция полезна для использования библиотек Swift, которые имеют автоматически сгенерированные заголовки Objective-C или классы зависимостей CocoaPods, написанные на Swift.
Ранее инструмент cinterop не анализировал заголовки, которые зависели от модулей Objective-C через директиву @import. Причина заключалась в том, что ему не хватало поддержки опции -fmodules.
Начиная с Kotlin 1.8.20, вы можете использовать заголовки Objective-C с @import. Для этого передайте опцию -fmodules в файл определения компилятора как compilerOpts. Если вы используете интеграцию CocoaPods, укажите опцию cinterop в блоке конфигурации функции pod() следующим образом:
kotlin {
ios()
cocoapods {
summary = "CocoaPods test library"
homepage = "https://github.com/JetBrains/kotlin"
ios.deploymentTarget = "13.5"
pod("PodName") {
extraOpts = listOf("-compiler-option", "-fmodules")
}
}
}
Это была долгожданная функция, и мы приветствуем ваш отзыв о ней в YouTrack, чтобы помочь нам сделать её по умолчанию в будущих выпусках.
Поддержка режима только линковки в плагине Cocoapods Gradle
В Kotlin 1.8.20 вы можете использовать зависимости Pod с динамическими фреймворками только для линковки без генерации привязок cinterop. Это может быть полезно, когда привязки cinterop уже сгенерированы.
Рассмотрим проект с 2 модулями, библиотекой и приложением. Библиотека зависит от Pod, но не создает фреймворк, а только .klib. Приложение зависит от библиотеки и создает динамический фреймворк. В этом случае вам нужно связать этот фреймворк с Pod-зависимостями библиотеки, но вам не нужны привязки cinterop, поскольку они уже сгенерированы для библиотеки.
Чтобы включить функцию, используйте опцию linkOnly или свойство билдера при добавлении зависимости от Pod:
cocoapods {
summary = "CocoaPods test library"
homepage = "https://github.com/JetBrains/kotlin"
pod("Alamofire", linkOnly = true) {
version = "5.7.0"
}
}
Импорт расширений Objective-C как членов класса в UIKit
С Xcode 14.1 некоторые методы из классов Objective-C были перемещены в члены категорий. Это привело к генерации другого API Kotlin, и эти методы были импортированы как расширения Kotlin, а не как методы.
Вы, возможно, столкнулись с проблемами, связанными с этим, при переопределении методов, используя UIKit. Например, стало невозможно переопределять методы drawRect() или layoutSubviews() при наследовании от UIView в Kotlin.
Начиная с версии 1.8.20, члены категорий, объявленные в тех же заголовках, что и классы NSView и UIView, импортируются как члены этих классов. Это означает, что методы, наследующие от NSView и UIView, могут быть легко переопределены, как и любой другой метод.
Если всё пройдёт успешно, мы планируем включить это поведение по умолчанию для всех классов Objective-C.
Переопределение управления кэшем компилятора в компиляторе
Для ускорения развития кэшей компилятора мы перенесли управление кэшем компилятора из плагина Kotlin Gradle в компилятор Kotlin/Native. Это разблокирует работу над несколькими важными улучшениями, включая те, что связаны со временем компиляции и гибкостью кэша компилятора.
Если вы столкнётесь с проблемой и вам нужно вернуться к старому поведению, используйте свойство Gradle kotlin.native.cacheOrchestration=gradle.
Мы будем признательны за ваш отзыв на это в YouTrack.
Устаревание useLibraries() в плагине Cocoapods Gradle
Kotlin 1.8.20 начинает цикл устаревания функции useLibraries(), используемой в интеграции CocoaPods для статических библиотек.
Мы представили функцию useLibraries() для поддержки зависимостей от Pods, содержащих статические библиотеки. Со временем этот случай стал очень редким. Большинство Pods распространяются по исходникам, и Objective-C фреймворки или XCFrameworks являются распространённым выбором для двоичного распространения.
Поскольку эта функция не популярна и создаёт проблемы, усложняющие разработку плагина Kotlin CocoaPods Gradle, мы решили её устареть.
Дополнительную информацию о фреймворках и XCFrameworks см. в Раздел сборки конечных нативных библиотек.
Kotlin Multiplatform
Kotlin 1.8.20 стремится улучшить опыт разработчика с помощью следующих обновлений для Kotlin Multiplatform:
Новый подход к иерархии наборов исходных кодов
Kotlin 1.8.20 предлагает новый способ настройки иерархии наборов исходных кодов в ваших проектах multiplatform — иерархия целевых платформ по умолчанию. Новый подход призван заменить сокращения целевых платформ, такие как ios, у которых есть проектные недостатки.
Идея, лежащая в основе иерархии целевых платформ по умолчанию, проста: вы явно объявляете все целевые платформы, для которых компилируется ваш проект, а плагин Kotlin Gradle автоматически создает общие наборы исходных кодов на основе указанных целевых платформ.
Настройка вашего проекта
Рассмотрим этот пример простого мобильного приложения multiplatform:
@OptIn(ExperimentalKotlinGradlePluginApi::class)
kotlin {
// Enable the default target hierarchy:
targetHierarchy.default()
android()
iosArm64()
iosSimulatorArm64()
}
Вы можете рассматривать иерархию целевых платформ по умолчанию как шаблон для всех возможных целевых платформ и их общих наборов исходных кодов. Когда вы объявляете конечные целевые платформы android, iosArm64, и iosSimulatorArm64 в своем коде, плагин Kotlin Gradle находит подходящие общие наборы исходных кодов из шаблона и создает их для вас. Результирующая иерархия выглядит следующим образом:
Зеленые наборы исходных кодов фактически создаются и присутствуют в проекте, а серые из шаблона по умолчанию игнорируются. Как видите, плагин Kotlin Gradle не создал набор исходных кодов watchos, например, потому что в проекте нет целевых платформ watchOS.
Если вы добавите целевую платформу watchOS, например watchosArm64, набор исходных кодов watchos создается, и код из наборов исходных кодов apple, native, и common компилируется в watchosArm64, также.
Полную схему иерархии целевых платформ по умолчанию можно найти в документации.
Почему заменить сокращения
Создание иерархий наборов исходных кодов может быть громоздким, подверженным ошибкам и неудобным для новичков. Наше предыдущее решение заключалось в представлении сокращений, таких как ios, которые создают часть иерархии за вас. Однако работа с сокращениями показала, что они имеют существенный проектный недостаток: их сложно изменить.
Например, рассмотрим сокращение ios. Оно создает только целевые платформы iosArm64 и iosX64, что может быть запутанным и может привести к проблемам при работе на хосте на базе M1, который также требует целевой платформы iosSimulatorArm64. Однако добавление целевой платформы iosSimulatorArm64 может быть очень значительным изменением для проектов пользователей:
Все зависимости, используемые в наборе исходных кодов
iosMain, должны поддерживать целевую платформуiosSimulatorArm64; в противном случае происходит сбой разрешения зависимостей.Некоторые нативные API, используемые в
iosMain, могут исчезнуть при добавлении новой целевой платформы (хотя это маловероятно в случаеiosSimulatorArm64).В некоторых случаях, например, при написании небольшого проекта на вашем MacBook на базе Intel, вам может даже не потребоваться это изменение.
Стало ясно, что сокращения не решали проблему конфигурирования иерархий, поэтому мы перестали добавлять новые сокращения на определенном этапе.
Иерархия целевых платформ по умолчанию может на первый взгляд выглядеть похожей на сокращения, но у них есть принципиальное различие: пользователи должны явно указать набор целевых платформ. Этот набор определяет, как компилируется и публикуется ваш проект, и как он участвует в разрешении зависимостей. Поскольку этот набор фиксирован, изменения в конфигурации по умолчанию со стороны плагина Kotlin Gradle должны вызывать значительно меньше проблем в экосистеме, и будет намного проще предоставить инструментальную помощь в миграции.
Как включить иерархию по умолчанию
Эта новая функция — Экспериментальная. Для скриптов сборки Kotlin Gradle вам нужно включить @OptIn(ExperimentalKotlinGradlePluginApi::class).
Для получения дополнительной информации см. Иерархическая структура проекта.
Оставьте свой отзыв
Это существенное изменение для проектов multiplatform. Мы будем благодарны за ваш отзыв, чтобы сделать его еще лучше.
Предварительный просмотр поддержки составных сборков Gradle в Kotlin Multiplatform
Начиная с версии 1.8.20-RC2, Kotlin Multiplatform поддерживает составные сборки Gradle. Составные сборки позволяют включать сборки отдельных проектов или частей одного проекта в единую сборку.
Из-за некоторых технических проблем использование составных сборок Gradle с Kotlin Multiplatform поддерживалось лишь частично. Kotlin 1.8.20-RC2 содержит предварительный просмотр улучшенной поддержки, которая должна работать с большим разнообразием проектов. Чтобы попробовать, добавьте следующий параметр в свой gradle.properties:
kotlin.mpp.import.enableKgpDependencyResolution=true
Этот параметр включает предварительный просмотр нового режима импорта. Помимо поддержки составных сборок, он обеспечивает более плавный процесс импорта в проектах multiplatform, так как мы включили важные исправления ошибок и улучшения для повышения стабильности импорта.
Известные проблемы
Это всё ещё предварительная версия, которая нуждается в дальнейшей стабилизации, и вы можете столкнуться с некоторыми проблемами с импортом по пути. Вот некоторые известные проблемы, которые мы планируем исправить до финальной версии Kotlin 1.8.20:
Плагин Kotlin 1.8.20 для IntelliJ IDEA 2023.1 EAP пока не доступен. Несмотря на это, вы всё ещё можете установить версию плагина Kotlin Gradle на 1.8.20-RC2 и попробовать составные сборки в этой IDE.
Если ваши проекты включают сборки со указанным
rootProject.name, составные сборки могут не смочь разрешить метаданные Kotlin. Для решения проблемы и подробностей см. эту Youtrack-задачу.
Мы призываем вас попробовать и сообщить все обнаруженные проблемы на YouTrack, чтобы помочь нам сделать его стандартным в Kotlin 1.9.0.
Улучшенный вывод ошибок Gradle в Xcode
Если у вас возникли проблемы со сборкой ваших проектов multiplatform в Xcode, вы могли столкнуться с ошибкой "Command PhaseScriptExecution failed with a nonzero exit code". Это сообщение указывает на то, что вызов Gradle завершился неудачей, но оно не очень полезно при поиске проблемы.
Начиная с Kotlin 1.8.20-RC2, Xcode может анализировать вывод компилятора Kotlin/Native. Кроме того, в случае неудачи сборки Gradle вы увидите дополнительное сообщение об ошибке от исключения основной причины в Xcode. В большинстве случаев это поможет определить основную проблему.

Новое поведение включено по умолчанию для стандартных задач Gradle для интеграции с Xcode, таких как embedAndSignAppleFrameworkForXcode, которые могут подключить фреймворк iOS из вашего проекта multiplatform к приложению iOS в Xcode. Оно также может быть включено (или отключено) с помощью свойства Gradle kotlin.native.useXcodeMessageStyle.
Kotlin/JavaScript
Kotlin 1.8.20 изменяет способы генерации определений TypeScript. Также внесено изменение, призванное улучшить опыт отладки:
Удаление интеграции Dukat из плагина Gradle
В Kotlin 1.8.20 мы удалили нашу экспериментальную интеграцию Dukat из плагина Kotlin/JavaScript Gradle. Интеграция Dukat поддерживала автоматическое преобразование файлов описаний TypeScript (.d.ts) в внешние объявления Kotlin.
Вы по-прежнему можете преобразовать файлы описаний TypeScript (.d.ts) в внешние объявления Kotlin, используя наш инструмент Dukat.
Имена переменных и функций Kotlin в картах источников
Для улучшения отладки мы добавили возможность включения в карты источников имен переменных и функций, объявленных в Kotlin-коде. До версии 1.8.20 эти имена отсутствовали в картах источников, поэтому в отладчике всегда отображались имена переменных и функций сгенерированного JavaScript.
Настройка добавляемых элементов осуществляется с помощью sourceMapNamesPolicy в вашем файле Gradle build.gradle.kts, или опции компилятора -source-map-names-policy. В таблице ниже приведены возможные настройки:
Настройка |
Описание |
Пример вывода |
|---|---|---|
|
Добавляются имена переменных и простых имён функций. (По умолчанию) |
|
|
Добавляются имена переменных и полные имена функций. |
|
|
Имена переменных и функций не добавляются. |
Н/Д |
Пример конфигурации в файле build.gradle.kts показан ниже:
tasks.withType<org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile>().configureEach {
compilercompileOptions.sourceMapNamesPolicy.set(org.jetbrains.kotlin.gradle.dsl.JsSourceMapNamesPolicy.SOURCE_MAP_NAMES_POLICY_FQ_NAMES) // or SOURCE_MAP_NAMES_POLICY_NO, or SOURCE_MAP_NAMES_POLICY_SIMPLE_NAMES
}
Инструменты отладки, такие как те, что предоставляются в браузерах на основе Chromium, могут использовать исходные имена Kotlin из карты источников для улучшения читабельности трассировки стека. Удачной отладки!
Включение генерации файлов определений TypeScript
Ранее, если у вас был проект, создающий исполняемые файлы (binaries.executable()), компилятор Kotlin/JS IR собирал все объявления верхнего уровня, помеченные как @JsExport, и автоматически генерировал определения TypeScript в файле .d.ts.
Поскольку это не полезно для каждого проекта, поведение в Kotlin 1.8.20 было изменено. Если вы хотите генерировать определения TypeScript, вам необходимо явно настроить это в файле сборки Gradle. Добавьте generateTypeScriptDefinitions() в свой build.gradle.kts.file в разделе js. Например:
kotlin {
js {
binaries.executable()
browser {
}
generateTypeScriptDefinitions()
}
}
Gradle
Kotlin 1.8.20 полностью совместим с Gradle 6.8 по 7.6, за исключением некоторых исключительных случаев в плагине Multiplatform. Также можно использовать версии Gradle до последней версии релиза Gradle, но имейте в виду, что вы можете столкнуться с предупреждениями об устаревании или некоторые новые возможности Gradle могут не работать.
В этой версии внесены следующие изменения:
Точное резервное копирование выходных данных задач компиляции
Леничная инициализация задач Kotlin/JVM для всех версий Gradle
Нестандартное расположение каталога назначения задач компиляции
Возможность отказа от отчета аргументов компилятора в службу статистики HTTP
Новая выравнивание версий плагинов Gradle
Gradle предоставляет способ обеспечения того, что зависимости, которые должны работать вместе, всегда выровнены по версиям. Kotlin 1.8.20 также принял этот подход. Он работает по умолчанию, так что вам не нужно изменять или обновлять свою конфигурацию, чтобы его включить. Кроме того, вам больше не нужно прибегать к этому обходному решению для разрешения транзитивных зависимостей плагинов Kotlin Gradle.
Мы будем рады вашим отзывам об этой функции в YouTrack.
Новая по умолчанию инкрементальная компиляция JVM в Gradle
Новый подход к инкрементальной компиляции, который доступен с Kotlin 1.7.0, теперь работает по умолчанию. Вам больше не нужно указывать kotlin.incremental.useClasspathSnapshot=true в вашем gradle.properties для его включения.
Мы будем рады вашим отзывам. Вы можете отправить запрос в YouTrack.
Точное резервное копирование выходных данных задач компиляции
Начиная с Kotlin 1.8.20, вы можете включить точное резервное копирование, при котором будут резервироваться только те классы, которые Kotlin перекомпилирует во время инкрементальной компиляции. Полное и точное резервное копирование помогают запускать сборку инкрементально после ошибок компиляции. Точное резервное копирование также экономит время сборки по сравнению с полным резервным копированием. Полное резервное копирование может занимать заметное время сборки в больших проектах или если много задач создают резервные копии, особенно если проект расположен на медленном жестком диске.
Эта оптимизация является экспериментальной. Вы можете включить её, добавив свойство kotlin.compiler.preciseCompilationResultsBackup Gradle в файл gradle.properties.
kotlin.compiler.preciseCompilationResultsBackup=true
Пример использования точного резервного копирования в JetBrains
На следующих диаграммах вы можете увидеть примеры использования точного резервного копирования по сравнению с полным резервным копированием:

Первая и вторая диаграммы показывают, как точное резервное копирование в проекте Kotlin влияет на сборку плагина Kotlin Gradle:
После внесения небольшого изменения ABI — добавление нового публичного метода — в модуль, от которого зависят многие модули.
После внесения небольшого изменения, не затрагивающего ABI — добавление частной функции — в модуль, от которого не зависят другие модули.
Третья диаграмма показывает, как точное резервное копирование в проекте Space влияет на сборку веб-фронта после небольшого изменения, не затрагивающего ABI — добавление частной функции — в модуль Kotlin/JS, от которого зависят многие модули.
Эти измерения были проведены на компьютере с процессором Apple M1 Max; на разных компьютерах результаты будут немного отличаться. Факторы, влияющие на производительность, включают, но не ограничиваются:
Насколько «теплые» демон Kotlin и демон Gradle.
Насколько быстрым или медленным является диск.
Модель процессора и насколько он загружен.
Какие модули затронуты изменениями и насколько они велики.
Являются ли изменения ABI или нет.
Оценивание оптимизаций с помощью отчетов о сборке
Чтобы оценить влияние оптимизации на ваш компьютер для вашего проекта и ваших сценариев, вы можете использовать отчеты о сборке Kotlin. Включите отчеты в текстовом формате, добавив следующее свойство в ваш gradle.properties файл:
kotlin.build.report.output=file
Вот пример релевантной части отчета до включения точного резервного копирования:
Task ':kotlin-gradle-plugin:compileCommonKotlin' finished in 0.59 s <...> Time metrics: Total Gradle task time: 0.59 s Task action before worker execution: 0.24 s Backup output: 0.22 s // Pay attention to this number <...>
А вот пример релевантной части отчета после включения точного резервного копирования:
Task ':kotlin-gradle-plugin:compileCommonKotlin' finished in 0.46 s <...> Time metrics: Total Gradle task time: 0.46 s Task action before worker execution: 0.07 s Backup output: 0.05 s // The time has reduced Run compilation in Gradle worker: 0.32 s Clear jar cache: 0.00 s Precise backup output: 0.00 s // Related to precise backup Cleaning up the backup stash: 0.00 s // Related to precise backup <...>
Леничная инициализация задач Kotlin/JVM для всех версий Gradle
Для проектов с плагином «org.jetbrains.kotlin.gradle.jvm» в Gradle 7.3+ плагин Kotlin Gradle больше не создаёт и не настраивает задачу «compileKotlin» сразу. В более старых версиях Gradle он просто регистрирует все задачи и не настраивает их при пробном запуске. Теперь такое же поведение используется и при использовании Gradle 7.3+.
Нестандартное расположение каталога назначения задач компиляции
Обновите скрипт сборки с дополнительным кодом, если вы делаете одно из следующего:
Переопределите расположение
KotlinJvmCompile/KotlinCompileзадачи Kotlin/JVM.Используете устаревший вариант Kotlin/JS/Non-IR вариант и переопределите расположение задачи
Kotlin2JsCompile.
Вам необходимо явно добавить sourceSets.main.kotlin.classesDirectories в sourceSets.main.outputs в ваш JAR-файл:
tasks.jar(type: Jar) { from sourceSets.main.outputs from sourceSets.main.kotlin.classesDirectories }
Возможность отказа от отчета аргументов компилятора в службу статистики HTTP
Теперь вы можете контролировать, должен ли плагин Kotlin Gradle включать аргументы компилятора в HTTP отчёты о сборке. Иногда вам может не понадобиться, чтобы плагин сообщал эти аргументы. Если проект содержит много модулей, его аргументы компилятора в отчёте могут быть очень объёмными и не очень полезными. Теперь есть способ отключить это и тем самым сэкономить память. В вашем gradle.properties или local.properties, используйте свойство kotlin.build.report.include_compiler_arguments=(true|false).
Мы будем рады вашим отзывам об этой функции в YouTrack.
Стандартная библиотека
Kotlin 1.8.20 добавляет множество новых функций, в том числе некоторые, особенно полезные для разработки Kotlin/Native:
Поддержка интерфейса AutoCloseable
Интерфейс AutoCloseable добавлен в общую стандартную библиотеку, чтобы вы могли использовать один общий интерфейс для закрытия ресурсов во всех библиотеках. В Kotlin/JVM интерфейс AutoCloseable является алиасом для java.lang.AutoClosable.
Кроме того, теперь включена функция расширения use(), которая выполняет заданный блок функции над выбранным ресурсом и затем корректно закрывает его, независимо от того, было ли брошено исключение или нет.
В общей стандартной библиотеке нет публичного класса, реализующего интерфейс AutoCloseable. В приведенном ниже примере мы определяем интерфейс XMLWriter и предполагаем, что существует ресурс, который его реализует. Например, этот ресурс может быть классом, открывающим файл, записывающим содержимое XML, а затем закрывающим его.
interface XMLWriter : AutoCloseable {
fun document(encoding: String, version: String, content: XMLWriter.() -> Unit)
fun element(name: String, content: XMLWriter.() -> Unit)
fun attribute(name: String, value: String)
fun text(value: String)
}
fun writeBooksTo(writer: XMLWriter) {
writer.use { xml ->
xml.document(encoding = "UTF-8", version = "1.0") {
element("bookstore") {
element("book") {
attribute("category", "fiction")
element("title") { text("Harry Potter and the Prisoner of Azkaban") }
element("author") { text("J. K. Rowling") }
element("year") { text("1999") }
element("price") { text("29.99") }
}
element("book") {
attribute("category", "programming")
element("title") { text("Kotlin in Action") }
element("author") { text("Dmitry Jemerov") }
element("author") { text("Svetlana Isakova") }
element("year") { text("2017") }
element("price") { text("25.19") }
}
}
}
}
}
Поддержка кодирования Base64
Мы добавили поддержку кодирования и декодирования Base64. Мы предоставляем 3 экземпляра классов, каждый из которых использует разные схемы кодирования и демонстрирует различное поведение. Используйте экземпляр Base64.Default для стандартной схемы кодирования Base64.
Используйте экземпляр Base64.UrlSafe для "безопасного" для URL и имён файлов кодирования.
Используйте экземпляр Base64.Mime для MIME схемы кодирования. При использовании экземпляра Base64.Mime все функции кодирования вставляют разделитель строки каждые 76 символов. В случае декодирования любые неверные символы пропускаются, и исключения не генерируются.
val foBytes = "fo".map { it.code.toByte() }.toByteArray()
Base64.Default.encode(foBytes) // "Zm8="
// Alternatively:
// Base64.encode(foBytes)
val foobarBytes = "foobar".map { it.code.toByte() }.toByteArray()
Base64.UrlSafe.encode(foobarBytes) // "Zm9vYmFy"
Base64.Default.decode("Zm8=") // foBytes
// Alternatively:
// Base64.decode("Zm8=")
Base64.UrlSafe.decode("Zm9vYmFy") // foobarBytes
Вы можете использовать дополнительные функции для кодирования или декодирования байтов в существующий буфер, а также для добавления результата кодирования к предоставленному объекту типа Appendable.
В Kotlin/JVM мы также добавили функции расширения encodingWith() и decodingWith() для выполнения кодирования и декодирования Base64 с входными и выходными потоками.
Поддержка @Volatile в Kotlin/Native
Если вы аннотируете свойство var с @Volatile, то вспомогательное поле отмечается таким образом, что любые чтения или записи в это поле являются атомными, а записи всегда делаются видимыми для других потоков.
До версии 1.8.20 аннотация kotlin.jvm.Volatile была доступна только в общей стандартной библиотеке. Однако эта аннотация эффективна только в JVM. Если вы используете ее в Kotlin/Native, она игнорируется, что может привести к ошибкам.
В 1.8.20 мы представили общую аннотацию kotlin.concurrent.Volatile, которую можно использовать как в JVM, так и в Kotlin/Native.
Как включить
Чтобы попробовать эту функцию, включите @OptIn(ExperimentalStdlibApi) и активируйте опцию компилятора -language-version 1.9. В проекте Gradle вы можете сделать это, добавив следующее в ваш build.gradle(.kts):
tasks
.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask<*>>()
.configureEach {
compilerOptions
.languageVersion
.set(
org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9
)
}
tasks
.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask.class)
.configureEach {
compilerOptions.languageVersion =
org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9
}
Исправление ошибки переполнения стека при использовании регулярных выражений в Kotlin/Native
В предыдущих версиях Kotlin могла возникнуть ошибка, если входные данные вашего регулярного выражения содержали большое количество символов, даже когда шаблон регулярного выражения был очень простым. В 1.8.20 эта проблема была решена. Дополнительную информацию см. в KT-46211.
Обновления сериализации
Kotlin 1.8.20 поставляется с альфа-поддержкой плагина Kotlin K2 компилятора для сериализации и запрещает настройку сериализатора через компаньон-объект.
Прототип плагина сериализации компилятора для Kotlin K2 компилятора
Начиная с версии 1.8.20, плагин компилятора сериализации работает с Kotlin K2 компилятором. Попробуйте и поделитесь своим отзывом!
Запрет неявной настройки сериализатора через компаньон-объект
В настоящее время можно объявить класс сериализуемым с аннотацией @Serializable и в то же время объявить пользовательского сериализатора с аннотацией @Serializer в его компаньон-объекте.
Например:
import kotlinx.serialization.*
@Serializable
class Foo(val a: Int) {
@Serializer(Foo::class)
companion object {
// Custom implementation of KSerializer<Foo>
}
}
В этом случае неясно из аннотации @Serializable, какой сериализатор используется. Фактически, у класса Foo есть пользовательский сериализатор.
Чтобы предотвратить такую путаницу, в Kotlin 1.8.20 мы ввели предупреждение компилятора в таких сценариях. Предупреждение включает возможный путь миграции для решения этой проблемы.
Если вы используете такие конструкции в своем коде, рекомендуем обновить их следующим образом:
import kotlinx.serialization.*
@Serializable(Foo.Companion::class)
class Foo(val a: Int) {
// Doesn't matter if you use @Serializer(Foo::class) or not
companion object: KSerializer<Foo> {
// Custom implementation of KSerializer<Foo>
}
}
С этим подходом ясно, что класс Foo использует пользовательского сериализатора, объявленного в компаньон-объекте. Дополнительную информацию см. в нашей YouTrack записи.
Обновления документации
Документация Kotlin претерпела некоторые заметные изменения:
Начало работы с Spring Boot и Kotlin – создайте простое приложение с базой данных и узнайте больше о возможностях Spring Boot и Kotlin.
Функции области видимости – узнайте, как упростить свой код с помощью полезных функций области видимости из стандартной библиотеки.
Интеграция CocoaPods – настройте среду для работы с CocoaPods.
Установка Kotlin 1.8.20
Проверка версии IDE
IntelliJ IDEA 2022.2 и 2022.3 автоматически предложат обновить плагин Kotlin до версии 1.8.20. IntelliJ IDEA 2023.1 имеет встроенный плагин Kotlin 1.8.20.
Android Studio Flamingo (222) и Giraffe (223) будут поддерживать Kotlin 1.8.20 в следующих релизах.
Новый компилятор командной строки доступен для скачивания на странице релиза GitHub.
Настройка настроек Gradle
Чтобы правильно загрузить артефакты и зависимости Kotlin, обновите свой файл settings.gradle(.kts) для использования репозитория Maven Central:
pluginManagement {
repositories {
mavenCentral()
gradlePluginPortal()
}
}
Если репозиторий не указан, Gradle использует устаревший репозиторий JCenter, что может привести к проблемам с артефактами Kotlin.
© 2010–2023 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/whatsnew1820.html