Spec-Zone.ru › Kotlin 1.7

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

Дата выпуска: 9 июня 2022 года

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

Вот список основных обновлений в этой версии:

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

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

  • Мы стабилизировали аннотации с требованиями к опциональному включению, типы, определенно не являющиеся nullable и инференс билдеров.

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

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

Вы также можете найти краткий обзор изменений в этом видео:

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

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

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

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

  • Компилятор K2: общий вид сверху

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

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

Проект

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

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

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

Kotlin

2.2 KLOC/с

4.8 KLOC/с

~ в 2.2 раза

YouTrack

1.8 KLOC/с

4.2 KLOC/с

~ в 2.3 раза

IntelliJ IDEA

1.8 KLOC/с

3.9 KLOC/с

~ в 2.2 раза

Space

1.2 KLOC/с

2.8 KLOC/с

~ в 2.3 раза

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

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

-Xuse-k2

Также компилятор K2 включает в себя ряд исправлений ошибок. Обратите внимание, что даже проблемы со статусом Открыто из этого списка фактически исправлены в K2.

Следующие релизы Kotlin улучшат стабильность компилятора K2 и добавят больше функций, так что следите за новостями!

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

Язык

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

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

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

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

  • Стабильные требования к опциональному включению

  • Стабильные типы, определенно не являющиеся nullable

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

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

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.

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

Стабильные требования к опциональному включению

Требования к опциональному включению теперь стабильны и не требуют дополнительной конфигурации компилятора.

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

Стабильные типы, определенно не являющиеся nullable

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

Вы можете пометить универсальный параметр типа как определенно не-nullable на месте использования с новым синтаксисом 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
}

Узнайте больше о типах, определенно не являющихся nullable, в этом KEEP.

Kotlin/JVM

Данный релиз включает улучшения производительности компилятора Kotlin/JVM и новый параметр компилятора. Кроме того, вызываемые ссылки на конструкторы функциональных интерфейсов стали стабильными. Обратите внимание, что с версии 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. Этот параметр аналогичен параметру командной строки javac --release. Параметр -Xjdk-release управляет целевой версией байткода и ограничивает API JDK в классе до указанной версии Java. Например, kotlinc -Xjdk-release=1.8 не позволит ссылаться на java.lang.Module даже если JDK в зависимостях имеет версию 9 или выше.

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

Пожалуйста, оставьте свои отзывы на этом тикете YouTrack.

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

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

Пожалуйста, сообщите о любых проблемах на 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 находится в стадии Альфа. Он может изменяться несовместимым образом и потребовать ручную миграцию в будущем. Мы будем рады вашим отзывам в YouTrack.

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

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

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

Единый ABI компилятора с JVM и JS IR бэкендами

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

Это улучшение очень полезно для авторов библиотек, так как оно улучшает опыт разработки плагинов компилятора. До этого выпуска вам приходилось предоставлять отдельные артефакты для 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 теперь возвращают тип Void вместо KotlinUnit в Swift. Это результат улучшенного взаимодействия с async/await Swift. Эта функция была введена в 1.6.20, и в этом релизе включена по умолчанию.

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

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

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

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

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

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

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

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

Ранее вам нужно было установить как менеджер зависимостей CocoaPods, так и плагин cocoapods-generate для использования CocoaPods, например, для управления зависимостями 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

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

Kotlin/JS

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

  • Улучшения производительности нового бекенда IR

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

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

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

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

Улучшения производительности нового бекенда IR

Этот релиз включает значительные обновления, которые должны улучшить ваш опыт разработки:

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

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

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

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

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

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

Этот тип минимизации автоматически применяется при сборке вашего Kotlin/JS-приложения в режиме производства и включён по умолчанию. Чтобы отключить минимизацию имён членов, используйте флаг компилятора -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, оператор import из JavaScript поддерживается в блоках js, позволяя вам динамически подключать пакеты в ваше приложение во время выполнения:

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() коллекций возвращают значения без возможности null

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

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

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

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

  • Отметки времени на основе встроенных классов для исходного источника времени по умолчанию

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

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

Функции min() и max() коллекций возвращают значения без возможности null

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

То же самое относилось к minBy(), maxBy(), minWith(), и maxWith(), которые все получили свои синонимы *OrNull() в Kotlin 1.4.0. Более старые функции, затронутые этим изменением, постепенно устарели.

Kotlin 1.7.0 повторно вводит оригинальные имена функций, но с типом возвращаемого значения без возможности null. Новые функции 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 вызовов.

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

Kotlin 1.7.0 улучшает производительность функциональности измерения времени, изменив отметки времени, возвращаемые TimeSource.Monotonic на встроенные классы значений. Это означает, что вызов функций, таких как 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 Optionals

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

Расширяющие функции 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

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

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

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

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

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

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

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

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

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

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

Мы ожидаем, что вы увидите наибольшую пользу от нового подхода, если используете кэш сборки или часто вносите изменения в не-Kotlin модули Gradle. Наши тесты для проекта 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 скана сборки build scan.

    Плагин 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.2

  • 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 для конфигурации Kotlin-компиляции внутри него, что означает, что вам не нужно добавлять плагин Kotlin Android Gradle в свой билд. Следите за объявлениями релизов плагина Android Gradle, чтобы узнать о добавленной поддержке и попробовать её!

Плагин 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. Узнайте больше об этом на странице Gradle.

END_OF_DOCUMENT_MARKER

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

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

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

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

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

Узнайте больше о требованиях к включению в Kotlin.

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

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

  • Опция компилятора kotlinOptions.jdkHome была устаревшей с 1.5.30 и удалена в этом релизе. Теперь сборки Gradle с этой опцией завершатся с ошибкой. Мы рекомендуем использовать Java toolchains, которые поддерживаются с 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.

Последнее изменение: 27 сентября 2022
Что нового в Kotlin 1.7.20 Что нового в Kotlin 1.6.20

© 2010–2022 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