Spec-Zone.ru › Kotlin 2

Что нового в Kotlin 1.8.0

Выпущен: 28 декабря 2022 года

Вышел Kotlin 1.8.0. Вот некоторые из главных нововведений:

  • Новые экспериментальные функции для JVM: рекурсивное копирование или удаление содержимого каталогов

  • Повышение производительности kotlin-reflect

  • Новый параметр компилятора -Xdebug для улучшения отладки

  • kotlin-stdlib-jdk7 и kotlin-stdlib-jdk8 объединены в kotlin-stdlib

  • Улучшенная совместимость с Objective-C и Swift

  • Совместимость с Gradle 7.3

Информацию о цикле выпуска Kotlin см. в разделе Процесс выпуска Kotlin.

Поддержка IDE

Плагин Kotlin с поддержкой версии 1.8.0 доступен для:

IDE

Поддерживаемые версии

IntelliJ IDEA

2021.3, 2022.1, 2022.2

Android Studio

Electric Eel (221), Flamingo (222)

Вы можете обновить свои проекты до Kotlin 1.8.0 в IntelliJ IDEA 2022.3, не обновляя плагин IDE.

Чтобы перенести существующие проекты на Kotlin 1.8.0 в IntelliJ IDEA 2022.3, измените версию Kotlin на 1.8.0 и повторно импортируйте проект Gradle или Maven.

Kotlin/JVM

Начиная с версии 1.8.0, компилятор может генерировать классы с версией байт-кода, соответствующей JVM 19. Новая версия языка также включает:

  • Параметр компилятора для отключения генерации целей аннотаций JVM

  • Новый параметр компилятора -Xdebug для отключения оптимизаций

  • Удаление старого бэкенда

  • Поддержка аннотации Lombok @Builder

Возможность не генерировать цели аннотаций TYPE_USE и TYPE_PARAMETER

Если среди целей Kotlin-аннотации указана TYPE, эта аннотация отображается в java.lang.annotation.ElementType.TYPE_USE в списке целей Java-аннотаций. Это аналогично тому, как цель Kotlin TYPE_PARAMETER отображается в цель Java java.lang.annotation.ElementType.TYPE_PARAMETER. Это создает проблему для клиентов Android с уровнями API ниже 26, в API которых такие цели отсутствуют.

Начиная с Kotlin 1.8.0, можно использовать новый параметр компилятора -Xno-new-java-annotation-targets, чтобы не генерировать цели аннотаций TYPE_USE и TYPE_PARAMETER.

Новый параметр компилятора для отключения оптимизаций

В Kotlin 1.8.0 добавлен новый параметр компилятора -Xdebug, который отключает оптимизации и тем самым упрощает отладку. Пока этот параметр отключает для корутин функцию «was optimized out». В будущем, после добавления новых оптимизаций, этот параметр будет отключать и их.

Функция «was optimized out» оптимизирует переменные при использовании suspend-функций. Однако отлаживать код с оптимизированными переменными сложно, поскольку их значения не отображаются.

Никогда не используйте этот параметр в рабочей среде: отключение этой функции с помощью -Xdebug может привести к утечкам памяти.

Удаление старого бэкенда

В Kotlin 1.5.0 мы объявили, что бэкенд на основе IR стал стабильным. Это означало, что старый бэкенд из Kotlin 1.4.* устарел. В Kotlin 1.8.0 мы полностью удалили старый бэкенд. Вместе с ним удалены параметр компилятора -Xuse-old-backend и параметр Gradle useOldBackend.

Поддержка аннотации Lombok @Builder

Сообщество оставило так много голосов за задачу YouTrack Kotlin Lombok: поддержка генерируемых конструкторов (@Builder), что мы просто обязаны были добавить поддержку аннотации @Builder.

Пока мы не планируем поддерживать аннотации @SuperBuilder или @Tolerate, но пересмотрим это решение, если за задачи @SuperBuilder и @Tolerate проголосует достаточно людей.

Узнайте, как настроить плагин компилятора Lombok.

Kotlin/Native

В Kotlin 1.8.0 добавлены изменения, касающиеся совместимости с Objective-C и Swift, поддержка Xcode 14.1 и улучшения плагина CocoaPods Gradle:

  • Поддержка Xcode 14.1

  • Улучшенная совместимость с Objective-C и Swift

  • Динамические фреймворки по умолчанию в плагине CocoaPods Gradle

Поддержка Xcode 14.1

Теперь компилятор Kotlin/Native поддерживает последнюю стабильную версию Xcode — 14.1. Улучшения совместимости включают следующие изменения:

  • Добавлен новый пресет watchosDeviceArm64 для целевой платформы watchOS, который поддерживает Apple watchOS на платформах ARM64.

  • В плагине Kotlin CocoaPods Gradle по умолчанию больше не встраивается битовый код во фреймворки Apple.

  • Библиотеки платформ обновлены с учетом изменений во фреймворках Objective-C для целевых платформ Apple.

Улучшенная совместимость с Objective-C и Swift

Чтобы улучшить совместимость Kotlin с Objective-C и Swift, добавлены три новые аннотации:

  • @ObjCName позволяет указать более идиоматичное имя в Swift или Objective-C вместо переименования объявления Kotlin.

    Аннотация указывает компилятору Kotlin использовать пользовательское имя Objective-C и Swift для этого класса, свойства, параметра или функции:

    @ObjCName(swiftName = "MySwiftArray")
    class MyKotlinArray {
        @ObjCName("index")
        fun indexOf(@ObjCName("of") element: String): Int = TODO()
    }
    
    // Usage with the ObjCName annotations
    let array = MySwiftArray()
    let index = array.index(of: "element")
    
  • @HiddenFromObjC позволяет скрыть объявление Kotlin от Objective-C.

    Аннотация указывает компилятору Kotlin не экспортировать функцию или свойство в Objective-C, а следовательно, и в Swift. Это позволяет сделать код Kotlin более удобным для использования из Objective-C и Swift.

  • @ShouldRefineInSwift полезна для замены объявления Kotlin оберткой, написанной на Swift.

    Аннотация указывает компилятору Kotlin пометить функцию или свойство как swift_private в сгенерированном API Objective-C. Такие объявления получают префикс __, из-за которого они не видны в коде Swift.

    Эти объявления по-прежнему можно использовать в коде Swift для создания удобного для Swift API, но, например, Xcode не будет предлагать их в автодополнении.

    Подробнее об уточнении объявлений Objective-C в Swift см. в официальной документации Apple.

Для использования новых аннотаций требуется явное согласие.

Команда Kotlin выражает большую благодарность Рику Клефасу за реализацию этих аннотаций.

Динамические фреймворки по умолчанию в плагине CocoaPods Gradle

Начиная с Kotlin 1.8.0, фреймворки Kotlin, зарегистрированные плагином CocoaPods Gradle, по умолчанию связываются динамически. Предыдущая статическая реализация не соответствовала поведению плагина Kotlin Gradle.

kotlin {
    cocoapods {
        framework {
            baseName = "MyFramework"
            isStatic = false // Now dynamic by default
        }
    }
}

Если в существующем проекте используется статический тип связывания и вы обновите проект до Kotlin 1.8.0 (или явно измените тип связывания), при выполнении проекта может возникнуть ошибка. Чтобы устранить ее, закройте проект Xcode и выполните pod install в каталоге Podfile.

Подробнее см. в справочнике DSL плагина CocoaPods Gradle.

Kotlin Multiplatform: новая структура наборов исходного кода Android

В Kotlin 1.8.0 представлена новая структура наборов исходного кода Android, заменяющая прежнюю схему именования каталогов, которая во многих отношениях была неудобной.

Рассмотрим пример двух каталогов androidTest, созданных в текущей структуре. Один предназначен для KotlinSourceSets, а другой — для AndroidSourceSets:

  • У них разная семантика: androidTest в Kotlin относится к типу unitTest, а в Android — к типу integrationTest.

  • Они создают запутанную структуру SourceDirectories, поскольку у src/androidTest/kotlin есть UnitTest, а у src/androidTest/java — InstrumentedTest.

  • И KotlinSourceSets, и AndroidSourceSets используют схожую схему именования конфигураций Gradle, поэтому результирующие конфигурации androidTest для наборов исходного кода Kotlin и Android совпадают: androidTestImplementation, androidTestApi, androidTestRuntimeOnly и androidTestCompileOnly.

Чтобы устранить эти и другие существующие проблемы, мы представили новую структуру наборов исходного кода Android. Вот некоторые ключевые различия между двумя структурами:

Схема именования KotlinSourceSet

Текущая структура наборов исходного кода

Новая структура наборов исходного кода

targetName + AndroidSourceSet.name

targetName + AndroidVariantType

{AndroidSourceSet.name} сопоставляется с {KotlinSourceSet.name} следующим образом:

Текущая структура наборов исходного кода

Новая структура наборов исходного кода

main

androidMain

androidMain

test

androidTest

androidUnitTest

androidTest

androidAndroidTest

androidInstrumentedTest

SourceDirectories

Текущая структура наборов исходного кода

Новая структура наборов исходного кода

Структура добавляет дополнительные SourceDirectories /kotlin

src/{AndroidSourceSet.name}/kotlin, src/{KotlinSourceSet.name}/kotlin

{AndroidSourceSet.name} сопоставляется с {SourceDirectories included} следующим образом:

Текущая структура наборов исходного кода

Новая структура наборов исходного кода

main

src/androidMain/kotlin, src/main/kotlin, src/main/java

src/androidMain/kotlin, src/main/kotlin, src/main/java

test

src/androidTest/kotlin, src/test/kotlin, src/test/java

src/androidUnitTest/kotlin, src/test/kotlin, src/test/java

androidTest

src/androidAndroidTest/kotlin, src/androidTest/java

src/androidInstrumentedTest/kotlin, src/androidTest/java, src/androidTest/kotlin

Расположение файла AndroidManifest.xml

Текущая структура наборов исходного кода

Новая структура наборов исходного кода

src/{AndroidSourceSet.name}/AndroidManifest.xml

src/{KotlinSourceSet.name}/AndroidManifest.xml

{AndroidSourceSet.name} сопоставляется с {AndroidManifest.xml location} следующим образом:

Текущая структура наборов исходного кода

Новая структура наборов исходного кода

main

src/main/AndroidManifest.xml

src/androidMain/AndroidManifest.xml

debug

src/debug/AndroidManifest.xml

src/androidDebug/AndroidManifest.xml

Связь между тестами Android и общими тестами

Новая структура наборов исходного кода Android меняет связь между инструментированными тестами Android (в новой структуре они переименованы в androidInstrumentedTest) и общими тестами.

Ранее между androidAndroidTest и commonTest по умолчанию существовала связь dependsOn. На практике это означало следующее:

  • Код из commonTest был доступен в androidAndroidTest.

  • Для объявлений expect в commonTest требовались соответствующие реализации actual в androidAndroidTest.

  • Тесты, объявленные в commonTest, также запускались как инструментированные тесты Android.

В новой структуре наборов исходного кода Android связь dependsOn не добавляется по умолчанию. Если вам нужно прежнее поведение, объявите эту связь вручную в файле build.gradle.kts:

kotlin {
    // ...
    sourceSets {
        val commonTest by getting
        val androidInstrumentedTest by getting {
            dependsOn(commonTest)
        }
    }
}

Поддержка вариантов Android

Ранее плагин Kotlin Gradle заранее создавал наборы исходного кода, соответствующие наборам исходного кода Android с типами сборки debug и release или пользовательскими вариантами, например demo и full. К ним можно было обращаться с помощью конструкций вроде val androidDebug by getting { ... }.

В новой структуре наборов исходного кода Android эти наборы создаются на этапе afterEvaluate. Поэтому такие выражения становятся недопустимыми и приводят к ошибкам, например org.gradle.api.UnknownDomainObjectException: KotlinSourceSet with name 'androidDebug' not found.

Чтобы обойти эту проблему, используйте новый API invokeWhenCreated() в файле build.gradle.kts:

kotlin {
    // ...
    sourceSets.invokeWhenCreated("androidFreeDebug") {
        // ...
    }
}

Настройка и конфигурация

В будущих выпусках новая структура станет использоваться по умолчанию. Вы можете включить ее уже сейчас с помощью следующего параметра Gradle:

kotlin.mpp.androidSourceSetLayoutVersion=2

Для новой структуры требуется Android Gradle Plugin версии 7.0 или выше; она поддерживается в Android Studio 2022.3 и более поздних версиях.

Использование каталогов в прежнем стиле Android теперь не рекомендуется. В Kotlin 1.8.0 начинается цикл отказа от них: для текущей структуры будет выдаваться предупреждение. Его можно отключить с помощью следующего свойства Gradle:

kotlin.mpp.androidSourceSetLayoutVersion1.nowarn=true

Kotlin/JS

В Kotlin 1.8.0 бэкенд компилятора JS IR становится стабильным, а в скрипты сборки Gradle, связанные с JavaScript, добавляются новые возможности:

  • Стабильный бэкенд компилятора JS IR

  • Новые настройки для уведомлений об обновлении yarn.lock

  • Добавление браузеров в качестве целевых платформ тестирования через свойства Gradle

  • Новый способ добавления поддержки CSS в проект

Стабильный бэкенд компилятора JS IR

Начиная с этого выпуска, бэкенд компилятора Kotlin/JS на основе промежуточного представления (IR) является стабильным. Унификация инфраструктуры для всех трех бэкендов заняла некоторое время, но теперь все они работают с одним и тем же IR для кода Kotlin.

В связи со стабилизацией бэкенда компилятора JS IR старый бэкенд теперь объявлен устаревшим.

Инкрементальная компиляция включена по умолчанию вместе со стабильным компилятором JS IR.

Если вы все еще используете старый компилятор, переключите проект на новый бэкенд.

Новые настройки для уведомлений об обновлении yarn.lock

Если вы используете менеджер пакетов yarn, доступны три новых специальных настройки Gradle, которые могут уведомлять об обновлении файла yarn.lock. Эти настройки пригодятся, если нужно получать уведомления о незаметных изменениях yarn.lock во время сборки CI.

Новые свойства Gradle:

  • YarnLockMismatchReport — задает способ уведомления об изменениях в файле yarn.lock. Можно указать одно из следующих значений:

    • FAIL приводит к сбою соответствующей задачи Gradle. Это значение используется по умолчанию.

    • WARNING записывает сведения об изменениях в журнал предупреждений.

    • NONE отключает уведомления.

  • reportNewYarnLock — явно сообщает о недавно созданном файле yarn.lock. По умолчанию этот параметр отключен: обычно новый файл yarn.lock создается при первом запуске. Этот параметр можно использовать, чтобы проверить, был ли файл добавлен в репозиторий.

  • yarnLockAutoReplace — автоматически заменяет yarn.lock при каждом запуске задачи Gradle.

Чтобы использовать эти параметры, обновите файл скрипта сборки build.gradle.kts следующим образом:

import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockMismatchReport
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension

rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin::class.java) {
    rootProject.the<YarnRootExtension>().yarnLockMismatchReport =
        YarnLockMismatchReport.WARNING // NONE | FAIL
    rootProject.the<YarnRootExtension>().reportNewYarnLock = false // true
    rootProject.the<YarnRootExtension>().yarnLockAutoReplace = false // true
}

Добавление браузеров в качестве целевых платформ тестирования через свойства Gradle

Начиная с Kotlin 1.8.0, целевые платформы тестирования для разных браузеров можно задавать прямо в файле свойств Gradle. Это позволяет уменьшить размер скрипта сборки, поскольку больше не нужно перечислять все целевые платформы в build.gradle.kts.

С помощью этого свойства можно задать список браузеров для всех модулей, а затем добавить отдельные браузеры в скрипты сборки нужных модулей.

Например, следующая строка в файле свойств Gradle запускает тесты в Firefox и Safari для всех модулей:

kotlin.js.browser.karma.browsers=firefox,safari

Полный список допустимых значений свойства см. на GitHub.

Команда Kotlin выражает большую благодарность Мартинасy Петушке за реализацию этой возможности.

Новый способ добавления поддержки CSS в проект

В этом выпуске представлен новый способ добавления поддержки CSS в проект. Мы предполагаем, что это затронет многие проекты, поэтому не забудьте обновить файлы скриптов сборки Gradle, как описано ниже.

До Kotlin 1.8.0 для добавления поддержки CSS использовалось свойство cssSupport.enabled:

browser {
    commonWebpackConfig {
        cssSupport.enabled = true
    }
}

Теперь следует использовать метод enabled.set() в блоке cssSupport {}:

browser {
    commonWebpackConfig {
        cssSupport {
            enabled.set(true)
        }
    }
}

Gradle

Kotlin 1.8.0 полностью поддерживает Gradle версий 7.2 и 7.3. Вы также можете использовать версии Gradle вплоть до последнего выпуска, но имейте в виду, что при этом могут появиться предупреждения об устаревших функциях или некоторые новые функции Gradle могут не работать.

В этой версии появилось множество изменений:

  • Предоставление параметров компилятора Kotlin в виде ленивых свойств Gradle

  • Повышение минимальных поддерживаемых версий

  • Возможность отключить резервную стратегию Kotlin daemon

  • Использование последней версии kotlin-stdlib в транзитивных зависимостях

  • Обязательная проверка совместимости целевых версий JVM связанных задач компиляции Kotlin и Java

  • Разрешение транзитивных зависимостей плагинов Kotlin Gradle

  • Устаревшие функции и удаления

Предоставление параметров компилятора Kotlin в виде ленивых свойств Gradle

Чтобы предоставить доступные параметры компилятора Kotlin в виде ленивых свойств Gradle и лучше интегрировать их в задачи Kotlin, мы внесли множество изменений:

  • У задач компиляции появился новый входной параметр compilerOptions, похожий на существующий kotlinOptions, но использующий Property из Gradle Properties API в качестве возвращаемого типа:

    tasks.named("compileKotlin", org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile::class.java) {
        compilerOptions {
            useK2.set(true)
        }
    }
    
  • У задач Kotlin tools KotlinJsDce и KotlinNativeLink появился новый входной параметр toolOptions, похожий на существующий входной параметр kotlinOptions.

  • У новых входных параметров есть аннотация Gradle @Nested. Каждое свойство внутри входных параметров имеет соответствующую аннотацию Gradle, например @Input или @Internal.

  • В артефакте API плагина Kotlin Gradle появились два новых интерфейса:

    • org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask с входным параметром compilerOptions и методом compileOptions(). Все задачи компиляции Kotlin реализуют этот интерфейс.

    • org.jetbrains.kotlin.gradle.tasks.KotlinToolTask с входным параметром toolOptions и методом toolOptions(). Все задачи Kotlin tools — KotlinJsDce, KotlinNativeLink и KotlinNativeLinkArtifactTask — реализуют этот интерфейс.

  • В некоторых compilerOptions используются новые типы вместо типа String:

    • JvmTarget

    • KotlinVersion (для входных параметров apiVersion и languageVersion)

    • JsMainFunctionExecutionMode

    • JsModuleKind

    • JsSourceMapEmbedMode

    Например, можно использовать compilerOptions.jvmTarget.set(JvmTarget.JVM_11) вместо kotlinOptions.jvmTarget = "11".

    Типы kotlinOptions не изменились; внутри они преобразуются в типы compilerOptions.

  • API плагина Kotlin Gradle бинарно совместим с предыдущими выпусками. Однако в артефакте kotlin-gradle-plugin есть некоторые изменения, нарушающие совместимость исходного кода и ABI. Большинство этих изменений связано с добавлением дополнительных обобщённых параметров для некоторых внутренних типов. Одно важное изменение: задача KotlinNativeLink больше не наследует задачу AbstractKotlinNativeCompile.

  • KotlinJsCompilerOptions.outputFile и связанные с ним параметры KotlinJsOptions.outputFile объявлены устаревшими. Вместо них используйте входной параметр задачи Kotlin2JsCompile.outputFileProperty.

Плагин Kotlin Gradle по-прежнему добавляет DSL KotlinJvmOptions к расширению Android:

android { 
    kotlinOptions {
        jvmTarget = "11"
    }
}

Это будет изменено в рамках этой задачи, когда DSL compilerOptions будет добавлен на уровне модуля.

Ограничения

Входной параметр задачи kotlinOptions и DSL задачи kotlinOptions{...} находятся в режиме поддержки и будут объявлены устаревшими в следующих выпусках. Улучшения будут вноситься только в compilerOptions и toolOptions.

Вызов любого сеттера или геттера у kotlinOptions делегируется соответствующему свойству в compilerOptions. Это накладывает следующие ограничения:

  • compilerOptions и kotlinOptions нельзя изменять на этапе выполнения задачи (см. исключение в абзаце ниже).

  • freeCompilerArgs возвращает неизменяемый List<String>, поэтому, например, kotlinOptions.freeCompilerArgs.remove("something") завершится ошибкой.

Некоторые плагины, в том числе kotlin-dsl и Android Gradle plugin (AGP) с включённым Jetpack Compose, пытаются изменить атрибут freeCompilerArgs на этапе выполнения задачи. В Kotlin 1.8.0 мы добавили для них обходное решение. Оно позволяет любому скрипту сборки или плагину изменять kotlinOptions.freeCompilerArgs на этапе выполнения, но при этом выводит предупреждение в журнал сборки. Чтобы отключить это предупреждение, используйте новое свойство Gradle kotlin.options.suppressFreeCompilerArgsModificationWarning=true. В Gradle планируют добавить исправления для плагина kotlin-dsl и AGP с включённым Jetpack Compose.

Повышение минимальных поддерживаемых версий

Начиная с Kotlin 1.8.0, минимальная поддерживаемая версия Gradle — 6.8.3, а минимальная поддерживаемая версия Android Gradle plugin — 4.1.3.

См. документацию о совместимости плагина Kotlin Gradle с доступными версиями Gradle

Возможность отключить резервную стратегию Kotlin daemon

Появилось новое свойство Gradle kotlin.daemon.useFallbackStrategy со значением по умолчанию true. Если его значение равно false, сборка завершается с ошибкой при проблемах с запуском или взаимодействием с daemon. Кроме того, в задачах компиляции Kotlin появилось новое свойство useDaemonFallbackStrategy, которое имеет приоритет над свойством Gradle, если используются оба. Если для выполнения компиляции недостаточно памяти, в журналах появится соответствующее сообщение.

Резервная стратегия компилятора Kotlin запускает компиляцию вне Kotlin daemon, если с daemon возникает сбой. Если Gradle daemon запущен, компилятор использует стратегию «In process». Если Gradle daemon не запущен, компилятор использует стратегию «Out of process». Подробнее см. в разделе Стратегия выполнения компилятора. Обратите внимание: незаметный переход на другую стратегию может потреблять много системных ресурсов или приводить к недетерминированным сборкам. Подробнее см. в задаче YouTrack.

Использование последней версии kotlin-stdlib в транзитивных зависимостях

Если вы явно укажете в зависимостях версию Kotlin 1.8.0 или выше, например: implementation("org.jetbrains.kotlin:kotlin-stdlib:1.8.0"), плагин Kotlin Gradle будет использовать эту версию Kotlin для транзитивных зависимостей kotlin-stdlib-jdk7 и kotlin-stdlib-jdk8. Это позволяет избежать дублирования классов из разных версий stdlib (подробнее о объединении kotlin-stdlib-jdk7 и kotlin-stdlib-jdk8 в kotlin-stdlib). Это поведение можно отключить с помощью свойства Gradle kotlin.stdlib.jdk.variants.version.alignment:

kotlin.stdlib.jdk.variants.version.alignment=false

Если у вас возникли проблемы с согласованием версий, согласуйте все версии с помощью Kotlin BOM, объявив зависимость от платформы kotlin-bom в скрипте сборки:

implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.0"))

О других случаях и рекомендуемых решениях см. в документации.

Обязательная проверка целевых версий JVM связанных задач компиляции Kotlin и Java

Этот раздел относится к вашему проекту JVM, даже если исходные файлы написаны только на Kotlin и вы не используете Java.

Начиная с этого выпуска, значением по умолчанию для свойства kotlin.jvm.target.validation.mode для проектов с Gradle 8.0+ будет error (эта версия Gradle ещё не выпущена), и при несовместимости целевых версий JVM плагин завершит сборку с ошибкой.

Изменение значения по умолчанию с warning на error — подготовительный этап для плавного перехода на Gradle 8.0. Рекомендуем установить для этого свойства значение error и настроить набор инструментов или вручную согласовать версии JVM.

Подробнее о том, что может произойти, если не проверять совместимость целевых версий.

Разрешение транзитивных зависимостей плагинов Kotlin Gradle

В Kotlin 1.7.0 мы добавили поддержку вариантов плагинов Gradle. Из-за этих вариантов в classpath сборки могут присутствовать разные версии плагинов Kotlin Gradle, зависящие от разных версий некоторых зависимостей, обычно kotlin-gradle-plugin-api. Это может привести к проблеме разрешения зависимостей. В качестве обходного решения предлагаем использовать плагин kotlin-dsl.

Плагин kotlin-dsl в Gradle 7.6 зависит от плагина org.jetbrains.kotlin.plugin.sam.with.receiver:1.7.10, который зависит от kotlin-gradle-plugin-api:1.7.10. Если добавить плагин org.jetbrains.kotlin.gradle.jvm:1.8.0, эта транзитивная зависимость kotlin-gradle-plugin-api:1.7.10 может привести к ошибке разрешения зависимостей из-за несовпадения версий (1.8.0 и 1.7.10) и значений атрибута варианта org.gradle.plugin.api-version. В качестве обходного решения добавьте это ограничение, чтобы согласовать версии. Это решение может понадобиться до тех пор, пока мы не реализуем платформу согласования библиотек плагина Kotlin Gradle, которая включена в планы:

dependencies {
    constraints {
        implementation("org.jetbrains.kotlin:kotlin-sam-with-receiver:1.8.0")
    }
}

Это ограничение принудительно задаёт версию org.jetbrains.kotlin:kotlin-sam-with-receiver:1.8.0 для транзитивных зависимостей в classpath сборки. Подробнее о похожем случае в системе отслеживания ошибок Gradle.

Устаревшие функции и удаления

В Kotlin 1.8.0 продолжается цикл устаревания следующих свойств и методов:

  • В примечаниях к Kotlin 1.7.0 было указано, что в задаче KotlinCompile по-прежнему было устаревшее свойство Kotlin classpath, которое планировалось удалить в будущих выпусках. Теперь мы изменили уровень устаревания свойства classpath задачи KotlinCompile на error. Во всех задачах компиляции входной параметр libraries используется для списка библиотек, необходимых для компиляции.

  • Мы удалили свойство kapt.use.worker.api, позволявшее запускать kapt через Gradle Workers API. Начиная с Kotlin 1.3.70, kapt по умолчанию использует Gradle workers, и мы рекомендуем продолжать использовать этот способ.

  • В Kotlin 1.7.0 мы объявили о начале цикла устаревания свойства kotlin.compiler.execution.strategy. В этом выпуске мы удалили это свойство. Узнайте, как задать стратегию выполнения компилятора Kotlin другими способами.

Стандартная библиотека

Kotlin 1.8.0:

  • Обновляет целевую версию JVM для компиляции.

  • Стабилизирует несколько функций: преобразование TimeUnit между Java и Kotlin, cbrt(), функции-расширения Java Optionals.

  • Предоставляет предварительную версию сравниваемых и вычитаемых TimeMarks.

  • Включает экспериментальные функции-расширения для java.nio.file.path.

  • Представляет улучшенную производительность kotlin-reflect.

Обновлённая целевая версия JVM для компиляции

В Kotlin 1.8.0 стандартные библиотеки (kotlin-stdlib, kotlin-reflect и kotlin-script-*) компилируются с целевой версией JVM 1.8. Ранее стандартные библиотеки компилировались с целевой версией JVM 1.6.

Kotlin 1.8.0 больше не поддерживает целевые версии JVM 1.6 и 1.7. Поэтому больше не нужно отдельно объявлять kotlin-stdlib-jdk7 и kotlin-stdlib-jdk8 в скриптах сборки, так как содержимое этих артефактов объединено с kotlin-stdlib.

Если вы явно объявили kotlin-stdlib-jdk7 и kotlin-stdlib-jdk8 в качестве зависимостей в скриптах сборки, замените их на kotlin-stdlib.

Обратите внимание: смешивание разных версий артефактов stdlib может привести к дублированию или отсутствию классов. Чтобы этого избежать, плагин Kotlin Gradle может помочь вам согласовать версии stdlib.

cbrt()

Функция cbrt(), позволяющая вычислить действительный кубический корень из double или float, теперь имеет стабильный статус.

import kotlin.math.*

fun main() {
    val num = 27
    val negNum = -num

    println("The cube root of ${num.toDouble()} is: " +
            cbrt(num.toDouble()))
    println("The cube root of ${negNum.toDouble()} is: " +
            cbrt(negNum.toDouble()))
}

Преобразование TimeUnit между Java и Kotlin

Функции toTimeUnit() и toDurationUnit() в kotlin.time теперь имеют стабильный статус. Эти функции, представленные в Kotlin 1.6.0 в экспериментальном статусе, улучшают взаимодействие между Kotlin и Java. Теперь можно легко преобразовывать Java java.util.concurrent.TimeUnit в Kotlin kotlin.time.DurationUnit и обратно. Эти функции доступны только для JVM.

import kotlin.time.*

// For use from Java
fun wait(timeout: Long, unit: TimeUnit) {
    val duration: Duration = timeout.toDuration(unit.toDurationUnit())
    ...
}

Сравниваемые и вычитаемые TimeMarks

Новые возможности TimeMarks имеют экспериментальный статус. Чтобы использовать их, необходимо явно согласиться на использование с помощью @OptIn(ExperimentalTime::class) или @ExperimentalTime.

До Kotlin 1.8.0, если требовалось вычислить разницу во времени между несколькими TimeMarks и текущим моментом, можно было вызвать elapsedNow() только для одной TimeMark за раз. Из-за этого было сложно сравнивать результаты: два вызова функции elapsedNow() нельзя было выполнить точно в один и тот же момент.

В Kotlin 1.8.0 эту проблему можно решить, вычитая и сравнивая TimeMarks, полученные из одного источника времени. Теперь можно создать экземпляр TimeMark, обозначающий текущий момент, и вычитать из него другие TimeMarks. Таким образом, результаты этих вычислений гарантированно будут относиться друг к другу.

import kotlin.time.*
fun main() {
//sampleStart
    val timeSource = TimeSource.Monotonic
    val mark1 = timeSource.markNow()
    Thread.sleep(500) // Sleep 0.5 seconds
    val mark2 = timeSource.markNow()

    // Before 1.8.0
    repeat(4) { n ->
        val elapsed1 = mark1.elapsedNow()
        val elapsed2 = mark2.elapsedNow()

        // Difference between elapsed1 and elapsed2 can vary depending 
        // on how much time passes between the two elapsedNow() calls
        println("Measurement 1.${n + 1}: elapsed1=$elapsed1, " +
                "elapsed2=$elapsed2, diff=${elapsed1 - elapsed2}")
    }
    println()

    // Since 1.8.0
    repeat(4) { n ->
        val mark3 = timeSource.markNow()
        val elapsed1 = mark3 - mark1
        val elapsed2 = mark3 - mark2

        // Now the elapsed times are calculated relative to mark3, 
        // which is a fixed value
        println("Measurement 2.${n + 1}: elapsed1=$elapsed1, " +
                "elapsed2=$elapsed2, diff=${elapsed1 - elapsed2}")
    }
    // It's also possible to compare time marks with each other
    // This is true, as mark2 was captured later than mark1
    println(mark2 > mark1)
//sampleEnd
}

Эта новая возможность особенно полезна при вычислениях анимации, когда нужно вычислить разницу между несколькими TimeMarks, представляющими разные кадры, или сравнить их.

Рекурсивное копирование и удаление каталогов

Эти новые функции для java.nio.file.path имеют экспериментальный статус. Чтобы использовать их, необходимо явно согласиться на использование с помощью @OptIn(kotlin.io.path.ExperimentalPathApi::class) или @kotlin.io.path.ExperimentalPathApi. Также можно использовать параметр компилятора -opt-in=kotlin.io.path.ExperimentalPathApi.

Мы добавили две новые функции-расширения для java.nio.file.Path — copyToRecursively() и deleteRecursively(), которые позволяют рекурсивно выполнять следующие действия:

  • Копировать каталог и его содержимое в другое место.

  • Удалять каталог и его содержимое.

Эти функции могут быть очень полезны в процессе резервного копирования.

Обработка ошибок

С помощью copyToRecursively() можно определить, что должно произойти при возникновении исключения во время копирования, перегрузив лямбда-функцию onError:

sourceRoot.copyToRecursively(destinationRoot, followLinks = false,
    onError = { source, target, exception ->
        logger.logError(exception, "Failed to copy $source to $target")
        OnErrorResult.TERMINATE
    })

При использовании deleteRecursively(), если во время удаления файла или папки возникает исключение, этот файл или папка пропускается. После завершения удаления deleteRecursively() выбрасывает IOException, содержащее все возникшие исключения в качестве подавленных исключений.

Перезапись файлов

Если copyToRecursively() обнаружит, что файл уже существует в целевом каталоге, возникнет исключение. Чтобы вместо этого перезаписать файл, используйте перегрузку с аргументом overwrite и задайте ему значение true:

fun setUpEnvironment(projectDirectory: Path, fixtureName: String) {
    fixturesRoot.resolve(COMMON_FIXTURE_NAME)
        .copyToRecursively(projectDirectory, followLinks = false)
    fixturesRoot.resolve(fixtureName)
        .copyToRecursively(projectDirectory, followLinks = false,
            overwrite = true) // patches the common fixture
}

Пользовательская операция копирования

Чтобы задать собственную логику копирования, используйте перегрузку с дополнительным аргументом copyAction. С помощью copyAction можно передать лямбда-функцию с нужными действиями, например:

sourceRoot.copyToRecursively(destinationRoot, followLinks = false) { source, target ->
    if (source.name.startsWith(".")) {
        CopyActionResult.SKIP_SUBTREE
    } else {
        source.copyToIgnoringExistingDirectory(target, followLinks = false)
        CopyActionResult.CONTINUE
    }
}

Подробнее об этих функциях-расширениях см. в справочнике API.

Функции-расширения Java Optionals

Функции-расширения, представленные в Kotlin 1.7.0, теперь имеют стабильный статус. Они упрощают работу с классами Optional в Java. Их можно использовать на JVM для извлечения и преобразования объектов Optional, а также для сокращения кода при работе с Java API. Подробнее см. в разделе Что нового в Kotlin 1.7.0.

Повышение производительности kotlin-reflect

Учитывая, что kotlin-reflect теперь компилируется с целевой версией JVM 1.8, мы перенесли механизм внутреннего кэширования на Java ClassValue. Раньше мы кэшировали только KClass, а теперь также кэшируем KType и KDeclarationContainer. Эти изменения значительно повысили производительность при вызове typeOf().

Обновления документации

В документации Kotlin появились заметные изменения:

Обновлённые и новые страницы

  • Обзор Gradle — узнайте, как настраивать и собирать проект Kotlin с помощью системы сборки Gradle, а также о доступных параметрах компилятора, компиляции и кэшах в плагине Kotlin Gradle.

  • Обработка null в Java и Kotlin — узнайте, чем отличаются подходы Java и Kotlin к обработке переменных, которые могут иметь значение null.

  • Руководство по Lincheck — узнайте, как настроить и использовать платформу Lincheck для тестирования параллельных алгоритмов на JVM.

Новые и обновлённые руководства

  • Начало работы с Gradle и Kotlin/JVM — создайте консольное приложение с помощью IntelliJ IDEA и Gradle.

  • Создание мультиплатформенного приложения с помощью Ktor и SQLDelight — создайте мобильное приложение для iOS и Android с помощью Kotlin Multiplatform Mobile.

  • Начало работы с Kotlin Multiplatform — узнайте о кроссплатформенной мобильной разработке на Kotlin и создайте приложение, работающее на Android и iOS.

Установка Kotlin 1.8.0

IntelliJ IDEA 2021.3, 2022.1 и 2022.2 автоматически предлагают обновить плагин Kotlin до версии 1.8.0. В IntelliJ IDEA 2022.3 версия 1.8.0 плагина Kotlin будет включена в одном из ближайших обновлений.

Чтобы перенести существующие проекты на Kotlin 1.8.0 в IntelliJ IDEA 2022.3, измените версию Kotlin на 1.8.0 и повторно импортируйте проект Gradle или Maven.

Для Android Studio Electric Eel (221) и Flamingo (222) версия 1.8.0 плагина Kotlin будет доступна в ближайших обновлениях Android Studio. Новый компилятор командной строки можно скачать на странице релиза на GitHub.

Руководство по совместимости с Kotlin 1.8.0

Kotlin 1.8.0 — это выпуск с новыми возможностями, поэтому он может содержать изменения, несовместимые с кодом, написанным для предыдущих версий языка. Подробный список этих изменений приведен в руководстве по совместимости с Kotlin 1.8.0.

20 мая 2026 г.
Что нового в Kotlin 1.8.20Руководство по совместимости с Kotlin 1.8.x

© 2010–2026 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/whatsnew18.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API