Spec-Zone.ru › Kotlin 2

Что нового в Kotlin 1.7.0

Выпущена: 9 июня 2022 г.

Выпущена Kotlin 1.7.0. В этой версии представлена альфа-версия нового компилятора Kotlin/JVM K2, стабилизированы языковые возможности и повышена производительность платформ JVM, JS и Native.

Ниже перечислены основные обновления этой версии:

  • Новый компилятор Kotlin K2 теперь доступен в альфа-версии и обеспечивает значительный прирост производительности. Он доступен только для JVM, и с ним не работают плагины компилятора, в том числе kapt.

  • Новый подход к инкрементальной компиляции в Gradle. Теперь инкрементальная компиляция поддерживается и при изменениях в зависимых модулях, не написанных на Kotlin, и совместима с Gradle.

  • Стабилизированы аннотации требований opt-in, определённо не допускающие null типы и вывод типов в построителях.

  • Теперь для аргументов типов доступен оператор подчёркивания. С его помощью можно автоматически вывести тип аргумента, если указаны другие типы.

  • В этом выпуске добавлена возможность реализации через делегирование встроенному значению inline-класса. Теперь можно создавать лёгкие обёртки, которые в большинстве случаев не выделяют память.

Краткий обзор изменений также представлен в этом видео:

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

Новый компилятор Kotlin K2 для JVM в альфа-версии

В этом выпуске Kotlin представлена альфа-версия нового компилятора Kotlin K2. Новый компилятор призван ускорить разработку языковых возможностей, объединить все поддерживаемые Kotlin платформы, повысить производительность и предоставить API для расширений компилятора.

Мы уже опубликовали подробные объяснения о новом компиляторе и его преимуществах:

  • Путь к новому компилятору Kotlin

  • Компилятор K2: обзор сверху вниз

Важно отметить, что в альфа-версии нового компилятора K2 мы в первую очередь сосредоточились на повышении производительности, и он работает только с проектами JVM. Он не поддерживает Kotlin/JS, Kotlin/Native и другие мультиплатформенные проекты, а плагины компилятора, в том числе kapt, с ним не работают.

Наши тесты производительности показали впечатляющие результаты на внутренних проектах:

Проект

Производительность текущего компилятора Kotlin

Производительность нового компилятора Kotlin K2

Прирост производительности

Kotlin

2.2 KLOC/s

4.8 KLOC/s

~ x2.2

YouTrack

1.8 KLOC/s

4.2 KLOC/s

~ x2.3

IntelliJ IDEA

1.8 KLOC/s

3.9 KLOC/s

~ x2.2

Space

1.2 KLOC/s

2.8 KLOC/s

~ x2.3

Показатель производительности KLOC/s означает количество тысяч строк кода, обрабатываемых компилятором за секунду.

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

-Xuse-k2

Кроме того, компилятор K2 содержит ряд исправлений ошибок. Обратите внимание: проблемы из этого списка, у которых указано Состояние: открыта, на самом деле исправлены в K2.

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

Если вы столкнулись с проблемами производительности компилятора Kotlin K2, сообщите о них в нашем трекере задач.

Язык

В Kotlin 1.7.0 добавлена поддержка реализации через делегирование и новый оператор подчёркивания для аргументов типов. Кроме того, стабилизированы несколько языковых возможностей, представленных в качестве предварительных в предыдущих выпусках:

  • Реализация через делегирование встроенному значению inline-класса

  • Оператор подчёркивания для аргументов типов

  • Стабильный вывод типов в построителях

  • Стабильные требования opt-in

  • Стабильные определённо не допускающие null типы

Реализация через делегирование встроенному значению inline-класса

Если вы хотите создать лёгкую обёртку для значения или экземпляра класса, необходимо вручную реализовать все методы интерфейса. Реализация через делегирование решает эту проблему, но до версии 1.7.0 она не работала с inline-классами. Это ограничение снято, поэтому теперь можно создавать лёгкие обёртки, которые в большинстве случаев не выделяют память.

interface Bar {
    fun foo() = "foo"
}

@JvmInline
value class BarWrapper(val bar: Bar): Bar by bar

fun main() {
    val bw = BarWrapper(object: Bar {})
    println(bw.foo())
}

Оператор подчёркивания для аргументов типов

В Kotlin 1.7.0 добавлен оператор подчёркивания, _, для аргументов типов. С его помощью можно автоматически вывести аргумент типа, если указаны другие типы:

abstract class SomeClass<T> {
    abstract fun execute(): T
}

class SomeImplementation : SomeClass<String>() {
    override fun execute(): String = "Test"
}

class OtherImplementation : SomeClass<Int>() {
    override fun execute(): Int = 42
}

object Runner {
    inline fun <reified S: SomeClass<T>, T> run(): T {
        return S::class.java.getDeclaredConstructor().newInstance().execute()
    }
}

fun main() {
    // T is inferred as String because SomeImplementation derives from SomeClass<String>
    val s = Runner.run<SomeImplementation, _>()
    assert(s == "Test")

    // T is inferred as Int because OtherImplementation derives from SomeClass<Int>
    val n = Runner.run<OtherImplementation, _>()
    assert(n == 42)
}

Оператор подчёркивания можно использовать в любой позиции списка переменных для вывода аргумента типа.

Стабильный вывод типов в построителях

Вывод типов в построителях — это особый вид вывода типов, полезный при вызове обобщённых функций-построителей. Он помогает компилятору вывести аргументы типов вызова, используя информацию о типах других вызовов внутри его лямбда-аргумента.

Начиная с версии 1.7.0, вывод типов в построителях активируется автоматически, если стандартному выводу типов недостаточно информации о типе без указания параметра компилятора -Xenable-builder-inference, появившегося в версии 1.6.0.

Узнайте, как писать собственные обобщённые построители.

Стабильные требования opt-in

Требования opt-in теперь имеют статус Stable и не требуют дополнительной настройки компилятора.

До версии 1.7.0 для самой возможности opt-in требовалось указывать аргумент -opt-in=kotlin.RequiresOptIn, чтобы избежать предупреждения. Теперь он не требуется. Однако аргумент компилятора -opt-in по-прежнему можно использовать, чтобы включить opt-in для других аннотаций или модуля.

Стабильные определённо не допускающие null типы

В Kotlin 1.7.0 определённо не допускающие null типы получили статус Stable. Они обеспечивают лучшую совместимость при расширении обобщённых классов и интерфейсов Java.

Обобщённый параметр типа можно пометить как определённо не допускающий null в месте использования с помощью нового синтаксиса T & Any. Эта синтаксическая форма основана на обозначении для типов-пересечений и теперь ограничена параметром типа с nullable-верхними границами слева от & и non-nullable Any справа:

fun <T> elvisLike(x: T, y: T & Any): T & Any = x ?: y

fun main() {
    // OK
    elvisLike<String>("", "").length
    // Error: 'null' cannot be a value of a non-null type
    elvisLike<String>("", null).length

    // OK
    elvisLike<String?>(null, "").length
    // Error: 'null' cannot be a value of a non-null type
    elvisLike<String?>(null, null).length
}

Подробнее об определённо не допускающих null типах читайте в этом KEEP.

Kotlin/JVM

В этом выпуске повышена производительность компилятора Kotlin/JVM и добавлен новый параметр компилятора. Кроме того, ссылки на конструкторы функциональных интерфейсов получили статус Stable. Обратите внимание: начиная с версии 1.7.0, целевая версия по умолчанию для компиляции Kotlin/JVM — 1.8.

  • Оптимизация производительности компилятора

  • Новый параметр компилятора -Xjdk-release

  • Стабильные ссылки на конструкторы функциональных интерфейсов

  • Удалена целевая версия JVM 1.6

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

В Kotlin 1.7.0 повышена производительность компилятора Kotlin/JVM. Согласно нашим тестам, время компиляции в среднем сократилось на 10% по сравнению с Kotlin 1.6.0. Проекты с большим количеством встроенных функций, например проекты, использующие kotlinx.html, будут компилироваться быстрее благодаря улучшениям постобработки байт-кода.

Новый параметр компилятора: -Xjdk-release

В Kotlin 1.7.0 появился новый параметр компилятора — -Xjdk-release. Он аналогичен параметру командной строки --release команды javac. Параметр -Xjdk-release задаёт целевую версию байт-кода и ограничивает доступный в classpath API JDK указанной версией Java. Например, kotlinc -Xjdk-release=1.8 не позволит ссылаться на java.lang.Module, даже если версия JDK в зависимостях — 9 или выше.

Для каждого дистрибутива JDK не гарантируется эффективность этого параметра.

Оставьте отзыв в этой задаче YouTrack.

Стабильные ссылки на конструкторы функциональных интерфейсов

Ссылки на вызываемые объекты для конструкторов функциональных интерфейсов теперь имеют статус Stable. Узнайте, как перейти от интерфейса с функцией-конструктором к функциональному интерфейсу, используя ссылки на вызываемые объекты.

Сообщайте о найденных проблемах в YouTrack.

Удалена целевая версия JVM 1.6

Целевая версия по умолчанию для компиляции Kotlin/JVM — 1.8. Целевая версия 1.6 удалена.

Перейдите на целевую версию JVM 1.8 или выше. Узнайте, как обновить целевую версию JVM для:

  • Gradle

  • Maven

  • компилятора командной строки

Kotlin/Native

В Kotlin 1.7.0 внесены изменения в совместимость с Objective-C и Swift и стабилизированы возможности, появившиеся в предыдущих выпусках. Также повышена производительность нового менеджера памяти и добавлены другие обновления:

  • Повышение производительности нового менеджера памяти

  • Унификация ABI плагинов компилятора с бэкендами JVM и JS IR

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

  • Совместимость со Swift async/await: возвращается Void вместо KotlinUnit

  • Запрет необъявленных исключений при передаче через мосты Objective-C

  • Улучшенная интеграция с CocoaPods

  • Переопределение URL для загрузки компилятора Kotlin/Native

Повышение производительности нового менеджера памяти

Новый менеджер памяти Kotlin/Native находится в статусе Alpha. В будущем он может претерпеть несовместимые изменения и потребовать ручной миграции. Мы будем признательны за ваши отзывы в YouTrack.

Новый менеджер памяти всё ещё находится в статусе Alpha, но постепенно приближается к статусу Stable. В этом выпуске значительно повышена производительность нового менеджера памяти, особенно при сборке мусора (GC). В частности, параллельная реализация фазы очистки, добавленная в версии 1.6.20, теперь включена по умолчанию. Это помогает сократить время приостановки приложения во время GC. Новый планировщик GC эффективнее выбирает частоту сборки мусора, особенно для больших куч.

Кроме того, мы специально оптимизировали отладочные бинарные файлы, обеспечив правильный уровень оптимизации и оптимизацию на этапе компоновки в коде реализации менеджера памяти. Благодаря этому в наших тестах время выполнения отладочных бинарных файлов сократилось примерно на 30%.

Попробуйте новый менеджер памяти в своих проектах и поделитесь отзывами в YouTrack.

Унификация ABI плагинов компилятора с бэкендами JVM и JS IR

Начиная с Kotlin 1.7.0, плагин Kotlin Multiplatform для Gradle по умолчанию использует встраиваемый JAR-файл компилятора для Kotlin/Native. Эта возможность была анонсирована в версии 1.6.0 как экспериментальная, а теперь она получила статус Stable и готова к использованию.

Это улучшение особенно полезно авторам библиотек, поскольку упрощает разработку плагинов компилятора. До этого выпуска требовалось предоставлять отдельные артефакты для Kotlin/Native, а теперь для Native и других поддерживаемых платформ можно использовать одни и те же артефакты плагина компилятора.

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

Узнайте, как подготовить плагин к обновлению, в этой задаче YouTrack.

Поддержка автономных исполняемых файлов Android

В Kotlin 1.7.0 появилась полная поддержка генерации стандартных исполняемых файлов для целей Android Native. Эта возможность была добавлена в версии 1.6.20, а теперь включена по умолчанию.

Чтобы вернуться к прежнему поведению, при котором Kotlin/Native генерировал общие библиотеки, используйте следующую настройку:

binaryOptions["androidProgramType"] = "nativeActivity"

Совместимость со Swift async/await: возврат Void вместо KotlinUnit

Функции Kotlin suspend теперь возвращают в Swift тип Void вместо KotlinUnit. Это результат улучшенной совместимости с async/await в Swift. Эта возможность была добавлена в версии 1.6.20, а в этом выпуске новое поведение включено по умолчанию.

Теперь для возврата правильного типа такими функциями больше не требуется свойство kotlin.native.binary.unitSuspendFunctionObjCExport=proper.

Запрет необъявленных исключений при передаче через мосты Objective-C

При вызове кода Kotlin из Swift/Objective-C (или наоборот), если этот код выбрасывает исключение, оно должно быть обработано кодом, в котором возникло, если только вы явно не разрешили передачу исключений между языками с корректным преобразованием (например, с помощью аннотации @Throws).

Ранее в Kotlin существовало ещё одно непредусмотренное поведение: в некоторых случаях необъявленные исключения могли «просачиваться» из одного языка в другой. В Kotlin 1.7.0 эта проблема исправлена, и теперь такие случаи приводят к завершению программы.

Например, если в Kotlin есть лямбда { throw Exception() } и она вызывается из Swift, то в Kotlin 1.7.0 выполнение завершится, как только исключение достигнет кода Swift. В предыдущих версиях Kotlin такое исключение могло просочиться в код Swift.

Аннотация @Throws продолжает работать как прежде.

Улучшенная интеграция с CocoaPods

Начиная с Kotlin 1.7.0, для интеграции CocoaPods в проекты больше не нужно устанавливать плагин cocoapods-generate.

Ранее для работы с CocoaPods требовалось установить менеджер зависимостей CocoaPods и плагин cocoapods-generate, например для управления зависимостями iOS в проектах Kotlin Multiplatform Mobile.

Теперь настроить интеграцию с CocoaPods стало проще. Кроме того, мы решили проблему, из-за которой cocoapods-generate не удавалось установить в Ruby 3 и более поздних версиях. Теперь поддерживаются и последние версии Ruby, которые лучше работают на Apple M1.

Узнайте, как настроить первоначальную интеграцию с CocoaPods.

Переопределение URL для загрузки компилятора Kotlin/Native

Начиная с Kotlin 1.7.0, можно настроить URL для загрузки компилятора Kotlin/Native. Это полезно, если во внешней среде CI запрещены внешние ссылки.

Чтобы переопределить базовый URL по умолчанию https://download.jetbrains.com/kotlin/native/builds, укажите следующее свойство Gradle:

kotlin.native.distribution.baseDownloadUrl=https://example.com

Загрузчик добавит к этому базовому URL версию Native и целевую ОС, чтобы загрузить нужный дистрибутив компилятора.

Kotlin/JS

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

  • Повышение производительности нового бэкенда IR

  • Минификация имен членов при использовании IR

  • Поддержка старых браузеров с помощью полифилов в бэкенде IR

  • Динамическая загрузка модулей JavaScript из выражений js

  • Указание переменных окружения для средств запуска тестов JavaScript

Повышение производительности нового бэкенда IR

В этом выпуске появились важные обновления, которые должны сделать разработку удобнее:

  • Производительность инкрементальной компиляции Kotlin/JS значительно улучшилась. Сборка проектов JS занимает меньше времени. Во многих случаях инкрементная пересборка теперь примерно не уступает устаревшему бэкенду.

  • Финальный бандл Kotlin/JS занимает меньше места благодаря значительному уменьшению размера итоговых артефактов. Для некоторых крупных проектов мы измерили уменьшение размера production-бандла до 20% по сравнению с устаревшим бэкендом.

  • Проверка типов для интерфейсов ускорилась на порядки.

  • Kotlin генерирует более качественный код JS

Минификация имен членов при использовании IR

Компилятор Kotlin/JS IR теперь использует внутренние сведения о связях между классами и функциями Kotlin, чтобы применять более эффективную минификацию, сокращая имена функций, свойств и классов. Это уменьшает размер полученных приложений в бандле.

Минификация этого типа автоматически применяется при сборке приложения Kotlin/JS в production-режиме и включена по умолчанию. Чтобы отключить минификацию имен членов, используйте флаг компилятора -Xir-minimized-member-names:

kotlin {
    js(IR) {
        compilations.all {
            compileKotlinTask.kotlinOptions.freeCompilerArgs += listOf("-Xir-minimized-member-names=false")
        }
    }
}

Поддержка старых браузеров с помощью полифилов в бэкенде IR

Бэкенд компилятора IR для Kotlin/JS теперь включает те же полифилы, что и устаревший бэкенд. Благодаря этому код, скомпилированный новым компилятором, может выполняться в старых браузерах, которые не поддерживают все методы ES2015, используемые стандартной библиотекой Kotlin. В итоговый бандл включаются только полифилы, используемые проектом, что сводит к минимуму их возможное влияние на его размер.

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

Динамическая загрузка модулей JavaScript из выражений js

При работе с модулями JavaScript большинство приложений используют статический импорт, который описан в разделе Интеграция с модулями JavaScript. Однако в Kotlin/JS не было механизма динамической загрузки модулей JavaScript во время выполнения приложения.

Начиная с Kotlin 1.7.0, в блоках js поддерживается инструкция import из JavaScript, позволяющая динамически подключать пакеты к приложению во время выполнения:

val myPackage = js("import('my-package')")

Указание переменных окружения для средств запуска тестов JavaScript

Чтобы настроить разрешение пакетов Node.js или передать тестам Node.js внешнюю информацию, теперь можно указать переменные окружения, используемые средствами запуска тестов JavaScript. Чтобы задать переменную окружения, используйте функцию environment() с парой ключ-значение внутри блока testTask в скрипте сборки:

kotlin {
    js {
        nodejs {
            testTask {
                environment("key", "value")
            }
        }
    }
}

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

В Kotlin 1.7.0 в стандартную библиотеку внесен ряд изменений и улучшений. Они добавляют новые возможности, переводят экспериментальные функции в стабильный статус и обеспечивают единообразную поддержку именованных групп захвата для Native, JS и JVM:

  • Функции коллекций min() и max() возвращают не nullable-значения

  • Сопоставление регулярных выражений по заданным индексам

  • Расширенная поддержка предыдущих версий языка и API

  • Доступ к аннотациям через рефлексию

  • Стабильные глубоко рекурсивные функции

  • Метки времени на основе inline-классов для источника времени по умолчанию

  • Новые экспериментальные функции расширения для Java Optional

  • Поддержка именованных групп захвата в JS и Native

Функции коллекций min() и max() возвращают не nullable-значения

В Kotlin 1.4.0 функции коллекций min() и max() были переименованы в minOrNull() и maxOrNull(). Новые имена точнее отражают их поведение: они возвращают null, если коллекция-получатель пуста. Это также помогло привести поведение функций в соответствие с соглашениями об именовании, принятыми во всем API коллекций Kotlin.

То же относилось к функциям minBy(), maxBy(), minWith() и maxWith(), для которых в Kotlin 1.4.0 появились аналоги с суффиксом *OrNull(). Старые функции, затронутые этим изменением, постепенно объявлялись устаревшими.

В Kotlin 1.7.0 исходные имена функций возвращаются, но теперь у них не nullable-типы возвращаемых значений. Новые функции min(), max(), minBy(), maxBy(), minWith() и maxWith() теперь гарантированно возвращают элемент коллекции или выбрасывают исключение.

fun main() {
    val numbers = listOf<Int>()
    println(numbers.maxOrNull()) // "null"
    println(numbers.max()) // "Exception in... Collection is empty."
}

Сопоставление регулярных выражений по заданным индексам

Функции Regex.matchAt() и Regex.matchesAt(), появившиеся в версии 1.5.30, теперь имеют стабильный статус. Они позволяют проверить, есть ли точное совпадение с регулярным выражением в определенной позиции в String или CharSequence.

matchesAt() проверяет наличие совпадения и возвращает логическое значение:

fun main() {
    val releaseText = "Kotlin 1.7.0 is on its way!"
    // regular expression: one digit, dot, one digit, dot, one or more digits
    val versionRegex = "\\d[.]\\d[.]\\d+".toRegex()

    println(versionRegex.matchesAt(releaseText, 0)) // "false"
    println(versionRegex.matchesAt(releaseText, 7)) // "true"
}

matchAt() возвращает найденное совпадение или null, если совпадения нет:

fun main() {
    val releaseText = "Kotlin 1.7.0 is on its way!"
    val versionRegex = "\\d[.]\\d[.]\\d+".toRegex()

    println(versionRegex.matchAt(releaseText, 0)) // "null"
    println(versionRegex.matchAt(releaseText, 7)?.value) // "1.7.0"
}

Будем признательны за ваши отзывы по задаче в YouTrack.

Расширенная поддержка предыдущих версий языка и API

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

Начиная с Kotlin 1.7.0, мы поддерживаем три предыдущие версии языка и API вместо двух. Это означает, что Kotlin 1.7.0 поддерживает разработку библиотек для версий Kotlin вплоть до 1.4.0. Подробнее об обратной совместимости см. в разделе Варианты совместимости.

Доступ к аннотациям через рефлексию

Функция расширения KAnnotatedElement.findAnnotations(), впервые добавленная в версии 1.6.0, теперь имеет стабильный статус. Эта функция рефлексии возвращает все аннотации заданного типа для элемента, включая примененные по отдельности и повторяющиеся аннотации.

@Repeatable
annotation class Tag(val name: String)

@Tag("First Tag")
@Tag("Second Tag")
fun taggedFunction() {
    println("I'm a tagged function!")
}

fun main() {
    val x = ::taggedFunction
    val foo = x as KAnnotatedElement
    println(foo.findAnnotations<Tag>()) // [@Tag(name=First Tag), @Tag(name=Second Tag)]
}

Стабильные глубоко рекурсивные функции

Глубоко рекурсивные функции были доступны в экспериментальном режиме начиная с Kotlin 1.4.0, а теперь в Kotlin 1.7.0 они получили стабильный статус. С помощью DeepRecursiveFunction можно определить функцию, которая хранит свой стек в куче, а не использует фактический стек вызовов. Это позволяет выполнять очень глубокие рекурсивные вычисления. Чтобы вызвать глубоко рекурсивную функцию, invoke ее.

В этом примере глубоко рекурсивная функция используется для рекурсивного вычисления глубины бинарного дерева. Хотя эта функция вызывает саму себя 100 000 раз, исключение StackOverflowError не выбрасывается:

class Tree(val left: Tree?, val right: Tree?)

val calculateDepth = DeepRecursiveFunction<Tree?, Int> { t ->
    if (t == null) 0 else maxOf(
        callRecursive(t.left),
        callRecursive(t.right)
    ) + 1
}

fun main() {
    // Generate a tree with a depth of 100_000
    val deepTree = generateSequence(Tree(null, null)) { prev ->
        Tree(prev, null)
    }.take(100_000).last()

    println(calculateDepth(deepTree)) // 100000
}

Рассмотрите возможность использования глубоко рекурсивных функций в коде, если глубина рекурсии превышает 1000 вызовов.

Метки времени на основе inline-классов для источника времени по умолчанию

В Kotlin 1.7.0 повышена производительность функций измерения времени: метки времени, возвращаемые TimeSource.Monotonic, теперь представлены inline-классами-значениями. Это означает, что при вызове таких функций, как markNow(), elapsedNow(), measureTime() и measureTimedValue(), не создаются классы-обертки для их экземпляров TimeMark. Это может помочь свести к минимуму влияние измерений на производительность, особенно при измерении участка кода на критическом пути:

@OptIn(ExperimentalTime::class)
fun main() {
    val mark = TimeSource.Monotonic.markNow() // Returned `TimeMark` is inline class
    val elapsedDuration = mark.elapsedNow()
}

Эта оптимизация доступна, только если источник времени, из которого получается TimeMark, статически известен как TimeSource.Monotonic.

Новые экспериментальные функции расширения для Java Optional

В Kotlin 1.7.0 появились новые удобные функции, упрощающие работу с классами Optional в Java. Эти функции позволяют извлекать значения из объектов Optional и преобразовывать их в JVM, упрощая работу с API Java.

Функции расширения getOrNull(), getOrDefault() и getOrElse() позволяют получить значение Optional, если оно присутствует. В противном случае возвращается null, значение по умолчанию или значение, возвращаемое функцией, соответственно:

val presentOptional = Optional.of("I'm here!")

println(presentOptional.getOrNull())
// "I'm here!"

val absentOptional = Optional.empty<String>()

println(absentOptional.getOrNull())
// null
println(absentOptional.getOrDefault("Nobody here!"))
// "Nobody here!"
println(absentOptional.getOrElse {
    println("Optional was absent!")
    "Default value!"
})
// "Optional was absent!"
// "Default value!"

Функции расширения toList(), toSet() и asSequence() преобразуют значение присутствующего Optional в список, множество или последовательность либо возвращают пустую коллекцию. Функция расширения toCollection() добавляет значение Optional в уже существующую целевую коллекцию:

val presentOptional = Optional.of("I'm here!")
val absentOptional = Optional.empty<String>()
println(presentOptional.toList() + "," + absentOptional.toList())
// ["I'm here!"], []
println(presentOptional.toSet() + "," + absentOptional.toSet())
// ["I'm here!"], []
val myCollection = mutableListOf<String>()
absentOptional.toCollection(myCollection)
println(myCollection)
// []
presentOptional.toCollection(myCollection)
println(myCollection)
// ["I'm here!"]
val list = listOf(presentOptional, absentOptional).flatMap { it.asSequence() }
println(list)
// ["I'm here!"]

В Kotlin 1.7.0 эти функции расширения представлены в экспериментальном статусе. Подробнее о расширениях Optional можно узнать в этом KEEP. Как всегда, будем рады вашим отзывам в системе отслеживания ошибок Kotlin.

Поддержка именованных групп захвата в JS и Native

Начиная с Kotlin 1.7.0, именованные группы захвата поддерживаются не только в JVM, но и на платформах JS и Native.

Чтобы задать имя группе захвата, используйте синтаксис (?<name>group) в регулярном выражении. Чтобы получить текст, соответствующий группе, вызовите новую функцию MatchGroupCollection.get() и передайте ей имя группы.

Получение значения совпавшей группы по имени

Рассмотрим пример сопоставления координат города. Чтобы получить коллекцию групп, которым соответствует регулярное выражение, используйте groups. Сравните получение содержимого группы по ее номеру (индексу) и по имени с помощью value:

fun main() {
    val regex = "\\b(?<city>[A-Za-z\\s]+),\\s(?<state>[A-Z]{2}):\\s(?<areaCode>[0-9]{3})\\b".toRegex()
    val input = "Coordinates: Austin, TX: 123"
    val match = regex.find(input)!!
    println(match.groups["city"]?.value) // "Austin" — by name
    println(match.groups[2]?.value) // "TX" — by number
}

Обратные ссылки по имени

Теперь при создании обратных ссылок на группы также можно использовать их имена. Обратные ссылки соответствуют тому же тексту, который ранее совпал с группой захвата. Для этого используйте синтаксис \k<name> в регулярном выражении:

fun backRef() {
    val regex = "(?<title>\\w+), yes \\k<title>".toRegex()
    val match = regex.find("Do you copy? Sir, yes Sir!")!!
    println(match.value) // "Sir, yes Sir"
    println(match.groups["title"]?.value) // "Sir"
}

Именованные группы в выражениях замены

В выражениях замены можно использовать ссылки на именованные группы. Рассмотрим функцию replace(), которая заменяет все вхождения заданного регулярного выражения во входных данных выражением замены, и функцию replaceFirst(), которая заменяет только первое совпадение.

Вхождения ${name} в строке замены заменяются подпоследовательностями, соответствующими группам захвата с указанным именем. Сравните замену по ссылкам на группы, заданным по имени и по индексу:

fun dateReplace() {
    val dateRegex = Regex("(?<dd>\\d{2})-(?<mm>\\d{2})-(?<yyyy>\\d{4})")
    val input = "Date of birth: 27-04-2022"
    println(dateRegex.replace(input, "\${yyyy}-\${mm}-\${dd}")) // "Date of birth: 2022-04-27" — by name
    println(dateRegex.replace(input, "\$3-\$2-\$1")) // "Date of birth: 2022-04-27" — by number
}

Gradle

В этом выпуске представлены новые отчёты о сборке, поддержка вариантов плагинов Gradle, новая статистика в kapt и многое другое:

  • Новый подход к инкрементальной компиляции

  • Новые отчёты о сборке для отслеживания производительности компилятора

  • Изменения минимальных поддерживаемых версий Gradle и Android Gradle Plugin

  • Поддержка вариантов плагинов Gradle

  • Обновления API плагина Kotlin Gradle

  • Доступность плагина sam-with-receiver через API плагинов

  • Изменения задач компиляции

  • Новая статистика по файлам, созданным каждым процессором аннотаций в kapt

  • Устаревание системного свойства kotlin.compiler.execution.strategy

  • Удаление устаревших параметров, методов и плагинов

Новый подход к инкрементальной компиляции

Новый подход к инкрементальной компиляции имеет статус экспериментального. Его могут удалить или изменить в любой момент. Для использования требуется явное согласие (подробности см. ниже). Мы рекомендуем использовать его только для оценки, и будем признательны за ваши отзывы в YouTrack.

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

Мы ожидаем, что наибольшую пользу от нового подхода вы получите, если используете кэш сборки или часто вносите изменения в модули Gradle, не написанные на Kotlin. Наши тесты для проекта Kotlin в модуле kotlin-gradle-plugin показывают ускорение более чем на 80% при внесении изменений после попадания в кэш.

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

kotlin.incremental.useClasspathSnapshot=true

Новый подход к инкрементальной компиляции сейчас доступен только для бэкенда JVM в системе сборки Gradle.

Узнайте, как реализован новый подход к инкрементальной компиляции, в этой публикации в блоге.

Мы планируем стабилизировать эту технологию и добавить поддержку других бэкендов (например, JS) и систем сборки. Будем признательны, если вы сообщите в YouTrack о любых проблемах или необычном поведении, с которыми столкнётесь при использовании этой схемы компиляции. Спасибо!

Команда Kotlin выражает огромную благодарность Ивану Гавриловичу, Хунгу Нгуену, Седрику Шампо и другим внешним участникам за помощь.

Отчёты о сборке для задач компилятора Kotlin

Отчёты о сборке Kotlin имеют статус экспериментальных. Их могут удалить или изменить в любой момент. Для использования требуется явное согласие (подробности см. ниже). Используйте их только для оценки. Будем признательны за ваши отзывы о них в YouTrack.

В Kotlin 1.7.0 появились отчёты о сборке, которые помогают отслеживать производительность компилятора. Отчёты содержат длительность разных этапов компиляции и причины, по которым компиляция не могла выполняться в инкрементальном режиме.

Отчёты о сборке пригодятся при изучении проблем с задачами компилятора, например:

  • Если сборка Gradle занимает слишком много времени и вы хотите понять причину низкой производительности.

  • Если время компиляции одного и того же проекта отличается: иногда она занимает секунды, а иногда — минуты.

Чтобы включить отчёты о сборке, укажите в gradle.properties, куда сохранять их результаты:

kotlin.build.report.output=file

Доступны следующие значения и их комбинации:

  • file сохраняет отчёты о сборке в локальный файл.

  • build_scan сохраняет отчёты о сборке в разделе custom values отчёта о сборке.

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

  • http отправляет отчёты о сборке по HTTP(S). Метод POST отправляет метрики в формате JSON. Данные могут меняться от версии к версии. Текущую версию отправляемых данных можно посмотреть в репозитории Kotlin.

При анализе отчётов о длительной компиляции можно выявить два распространённых случая, решение которых поможет устранить проблему:

  • Сборка не была инкрементальной. Проанализируйте причины и устраните основные проблемы.

  • Сборка была инкрементальной, но заняла слишком много времени. Попробуйте реорганизовать исходные файлы: разделите большие файлы, размещайте отдельные классы в разных файлах, выполняйте рефакторинг крупных классов, объявляйте функции верхнего уровня в разных файлах и так далее.

Подробнее о новых отчётах о сборке читайте в этой публикации в блоге.

Вы можете попробовать использовать отчёты о сборке в своей инфраструктуре. Если у вас есть отзывы, вы столкнулись с проблемами или хотите предложить улучшения, сообщите об этом в нашем трекере задач. Спасибо!

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

Начиная с Kotlin 1.7.0 минимальная поддерживаемая версия Gradle — 6.7.1. Нам пришлось повысить версию, чтобы поддержать варианты плагинов Gradle и новый API Gradle. В будущем благодаря поддержке вариантов плагинов Gradle нам, вероятно, не придётся так часто повышать минимальную поддерживаемую версию.

Кроме того, теперь минимальная поддерживаемая версия Android Gradle Plugin — 3.6.4.

Поддержка вариантов плагинов Gradle

В Gradle 7.0 появилась новая функция для авторов плагинов Gradle — плагины с вариантами. Эта функция упрощает добавление поддержки новых возможностей Gradle с сохранением совместимости с версиями Gradle ниже 7.1. Подробнее о выборе вариантов в Gradle.

Благодаря вариантам плагинов Gradle мы можем выпускать разные варианты плагина Kotlin Gradle для разных версий Gradle. Цель — поддерживать базовую компиляцию Kotlin в варианте main, соответствующем самым старым поддерживаемым версиям Gradle. Каждый вариант будет реализовывать возможности Gradle, доступные в соответствующем выпуске. Последний вариант будет поддерживать самый широкий набор возможностей Gradle. Такой подход позволяет поддерживать более старые версии Gradle с ограниченной функциональностью.

Сейчас у плагина Kotlin Gradle есть только два варианта:

  • main для Gradle версий 6.7.1–6.9.3

  • gradle70 для Gradle версий 7.0 и выше

В следующих выпусках Kotlin могут появиться и другие варианты.

Чтобы проверить, какой вариант используется при сборке, включите уровень журнала --info и найдите в выводе строку, начинающуюся с Using Kotlin Gradle plugin, например Using Kotlin Gradle plugin main variant.

Ниже приведены способы решения некоторых известных проблем с выбором вариантов в Gradle:

  • ResolutionStrategy в pluginManagement не работает для плагинов с несколькими вариантами

  • Варианты плагинов игнорируются, если плагин добавлен как общая зависимость buildSrc

Оставьте отзыв в этой задаче YouTrack.

Обновления API плагина Kotlin Gradle

Артефакт API плагина Kotlin Gradle получил несколько улучшений:

  • Добавлены новые интерфейсы для задач Kotlin/JVM и Kotlin/kapt с входными данными, настраиваемыми пользователем.

  • Добавлен новый интерфейс KotlinBasePlugin, от которого наследуются все плагины Kotlin. Используйте этот интерфейс, если хотите запускать действие конфигурации при применении любого плагина Kotlin Gradle (JVM, JS, Multiplatform, Native и других платформ):

    project.plugins.withType<org.jetbrains.kotlin.gradle.plugin.KotlinBasePlugin>() {
        // Configure your action here
    }
    

    Вы можете оставить отзыв об KotlinBasePlugin в этой задаче YouTrack.

  • Мы заложили основу для того, чтобы Android Gradle Plugin мог настраивать компиляцию Kotlin самостоятельно. Это позволит не добавлять в сборку плагин Kotlin Android Gradle. Следите за объявлениями о выпусках Android Gradle Plugin, чтобы узнать о появлении поддержки и попробовать её!

Плагин sam-with-receiver доступен через API плагинов

Теперь плагин компилятора sam-with-receiver доступен через DSL плагинов Gradle:

plugins {
    id("org.jetbrains.kotlin.plugin.sam.with.receiver") version "$kotlin_version"
}

Изменения задач компиляции

В этом выпуске задачи компиляции претерпели множество изменений:

  • Задачи компиляции Kotlin больше не наследуются от задачи Gradle AbstractCompile. Они наследуются только от DefaultTask.

  • У задачи AbstractCompile есть входные данные sourceCompatibility и targetCompatibility. Поскольку задача AbstractCompile больше не является базовым классом, эти входные данные больше недоступны в скриптах пользователей Kotlin.

  • Входные данные SourceTask.stableSources больше недоступны; вместо них следует использовать sources. Методы setSource(...) по-прежнему доступны.

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

  • Задачи компиляции по-прежнему реализуют интерфейс PatternFilterable, позволяющий фильтровать исходные файлы Kotlin. Входные данные sourceFilesExtensions удалены; вместо них следует использовать методы PatternFilterable.

  • Устаревшие выходные данные Gradle destinationDir: File заменены выходными данными destinationDirectory: DirectoryProperty.

  • Задача Kotlin/Native AbstractNativeCompile теперь наследуется от базового класса AbstractKotlinCompileTool. Это первый шаг к интеграции инструментов сборки Kotlin/Native со всеми остальными инструментами.

Оставьте отзыв в этой задаче YouTrack.

Статистика по файлам, созданным каждым процессором аннотаций в kapt

Плагин Gradle kotlin-kapt уже предоставляет статистику производительности для каждого процессора. Начиная с Kotlin 1.7.0 он также может сообщать количество файлов, созданных каждым процессором аннотаций.

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

Включите статистику в два этапа:

  • Установите флаг showProcessorStats в значение true в файле build.gradle.kts:

    kapt {
        showProcessorStats = true
    }
    
  • Установите свойство Gradle kapt.verbose в значение true в файле gradle.properties:

    kapt.verbose=true
    

Также можно включить подробный вывод с помощью параметра командной строки verbose.

Статистика появится в журналах с уровнем info. Вы увидите строку Annotation processor stats:, за которой последует статистика времени выполнения каждого процессора аннотаций. После этих строк появится строка Generated files report:, за которой последует статистика количества файлов, созданных каждым процессором аннотаций. Например:

[INFO] Annotation processor stats:
[INFO] org.mapstruct.ap.MappingProcessor: total: 290 ms, init: 1 ms, 3 round(s): 289 ms, 0 ms, 0 ms
[INFO] Generated files report:
[INFO] org.mapstruct.ap.MappingProcessor: total sources: 2, sources per round: 2, 0, 0

Оставьте отзыв в этой задаче YouTrack.

Устаревание системного свойства kotlin.compiler.execution.strategy

В Kotlin 1.6.20 появились новые свойства для настройки стратегии выполнения компилятора Kotlin. В Kotlin 1.7.0 начался цикл устаревания старого системного свойства kotlin.compiler.execution.strategy в пользу новых свойств.

При использовании системного свойства kotlin.compiler.execution.strategy вы получите предупреждение. Это свойство будет удалено в следующих выпусках. Чтобы сохранить прежнее поведение, замените системное свойство свойством Gradle с тем же именем. Например, это можно сделать в gradle.properties:

kotlin.compiler.execution.strategy=out-of-process

Также можно использовать свойство задачи компиляции compilerExecutionStrategy. Подробнее см. на странице «Стратегия выполнения компилятора».

Удаление устаревших параметров, методов и плагинов

Удаление метода useExperimentalAnnotation

В Kotlin 1.7.0 завершился цикл устаревания метода Gradle useExperimentalAnnotation. Вместо него используйте optIn(), чтобы явно разрешить использование API в модуле.

Например, если ваш модуль Gradle является многоплатформенным:

sourceSets {
    all {
        languageSettings.optIn("org.mylibrary.OptInAnnotation")
    }
}

Подробнее о требованиях к явному разрешению использования API в Kotlin.

Удаление устаревших параметров компилятора

Завершился цикл устаревания нескольких параметров компилятора:

  • Параметр компилятора kotlinOptions.jdkHome был объявлен устаревшим в версии 1.5.30 и удалён в текущем выпуске. Теперь сборки Gradle завершаются с ошибкой, если содержат этот параметр. Рекомендуем использовать цепочки инструментов Java, поддерживаемые начиная с Kotlin 1.5.30.

  • Устаревший параметр компилятора noStdlib также удалён. Плагин Gradle использует свойство kotlin.stdlib.default.dependency=true, чтобы определять, присутствует ли стандартная библиотека Kotlin.

Аргументы компилятора -jdkHome и -no-stdlib по-прежнему доступны.

Удаление устаревших плагинов

В Kotlin 1.4.0 плагины kotlin2js и kotlin-dce-plugin были объявлены устаревшими, а в этом выпуске удалены. Вместо kotlin2js используйте новый плагин org.jetbrains.kotlin.js. Удаление мёртвого кода (DCE) работает при правильной настройке плагина Kotlin/JS Gradle.

В Kotlin 1.6.0 мы изменили уровень устаревания класса KotlinGradleSubplugin на ERROR. Разработчики использовали этот класс для написания плагинов компилятора. В этом выпуске этот класс удалён. Вместо него используйте класс KotlinCompilerPluginSupportPlugin.

Рекомендуется использовать в проекте плагины Kotlin версии 1.7.0 или выше.

Удаление устаревшего параметра DSL и свойства для корутин

Мы удалили устаревший параметр DSL Gradle kotlin.experimental.coroutines и свойство kotlin.coroutines, использовавшееся в gradle.properties. Теперь можно просто использовать приостанавливаемые функции или добавить зависимость kotlinx.coroutines в скрипт сборки.

Подробнее о корутинах читайте в руководстве по корутинам.

Удаление приведения типа в методе расширения для цепочки инструментов

До Kotlin 1.7.0 при настройке цепочки инструментов Gradle с помощью Kotlin DSL требовалось привести тип к классу JavaToolchainSpec:

kotlin {
    jvmToolchain {
        (this as JavaToolchainSpec).languageVersion.set(JavaLanguageVersion.of(<MAJOR_JDK_VERSION>)
    }
}

Теперь часть (this as JavaToolchainSpec) можно опустить:

kotlin {
    jvmToolchain {
        languageVersion.set(JavaLanguageVersion.of(<MAJOR_JDK_VERSION>)
    }
}

Переход на Kotlin 1.7.0

Установка Kotlin 1.7.0

IntelliJ IDEA 2022.1 и Android Studio Chipmunk (212) автоматически предложат обновить плагин Kotlin до версии 1.7.0.

Для IntelliJ IDEA 2022.2, Android Studio Dolphin (213) и Android Studio Electric Eel (221) плагин Kotlin 1.7.0 будет поставляться с будущими обновлениями IntelliJ IDEA и Android Studio.

Новый компилятор командной строки можно скачать на странице выпуска на GitHub.

Переход существующего проекта или создание нового проекта на Kotlin 1.7.0

  • Чтобы перенести существующие проекты на Kotlin 1.7.0, измените версию Kotlin на 1.7.0 и повторно импортируйте проект Gradle или Maven. Узнайте, как обновиться до Kotlin 1.7.0.

  • Чтобы создать новый проект на Kotlin 1.7.0, обновите плагин Kotlin и запустите мастер создания проекта, выбрав Файл | Создать | Проект.

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

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

01 июля 2026
Что нового в Kotlin 1.7.20Руководство по совместимости Kotlin 1.7.20

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

Spec-Zone.ru

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