Что нового в Kotlin 1.7.0
Выпущен Kotlin 1.7.0. Он представляет собой альфа-версию нового компилятора Kotlin/JVM K2, стабилизирует языковые возможности и улучшает производительность для платформ JVM, JS и Native.
Вот список основных обновлений в этой версии:
Новый компилятор Kotlin K2 находится в альфа-версии, и он предлагает значительные улучшения производительности. Он доступен только для JVM, и ни один из плагинов компилятора, включая kapt, с ним не работает.
Новый подход к инкрементальной компиляции в Gradle. Инкрементальная компиляция теперь также поддерживается для изменений, внесенных внутри зависимых модулей, не написанных на Kotlin, и совместима с Gradle.
Мы стабилизировали аннотации требований к opt-in, типы, определенно не допускающие null и вывод типов для билдеров.
Теперь есть оператор подчеркивания для аргументов типов. Вы можете использовать его для автоматического вывода типа аргумента, когда указаны другие типы.
В этом выпуске разрешена реализация делегированием к встроенному значению встроенного класса. Теперь вы можете создавать лёгкие обёртки, которые в большинстве случаев не выделяют память.
Также вы можете найти краткий обзор изменений в этом видео:
Новый компилятор Kotlin K2 для JVM в альфа-версии
Этот выпуск Kotlin представляет альфа-версию нового компилятора Kotlin K2. Новый компилятор направлен на ускорение разработки новых языковых функций, унификацию всех поддерживаемых Kotlin платформ, улучшение производительности и предоставление API для расширений компилятора.
Мы уже опубликовали некоторые подробные объяснения нашего нового компилятора и его преимуществ:
Важно отметить, что в альфа-версии нового компилятора K2 мы в первую очередь сосредоточились на улучшении производительности, и он работает только с проектами JVM. Он не поддерживает Kotlin/JS, Kotlin/Native или другие многоплатформенные проекты, и ни один из плагинов компилятора, включая kapt, с ним не работает.
Наши бенчмарки показывают отличные результаты в наших внутренних проектах:
Проект |
Текущая производительность компилятора Kotlin |
Производительность нового компилятора Kotlin K2 |
Увеличение производительности |
|---|---|---|---|
Kotlin |
2,2 тыс. строк/с |
4,8 тыс. строк/с |
~ х2,2 |
YouTrack |
1,8 тыс. строк/с |
4,2 тыс. строк/с |
~ х2,3 |
IntelliJ IDEA |
1,8 тыс. строк/с |
3,9 тыс. строк/с |
~ х2,2 |
Space |
1,2 тыс. строк/с |
2,8 тыс. строк/с |
~ х2,3 |
Вы можете проверить повышение производительности в своих проектах JVM и сравнить его с результатами старого компилятора. Для активации компилятора Kotlin K2 используйте следующий параметр компилятора:
-Xuse-k2
Также компилятор K2 содержит ряд исправлений ошибок. Обратите внимание, что даже проблемы со статусом Открыто из этого списка на самом деле исправлены в K2.
Следующие релизы Kotlin улучшат стабильность компилятора K2 и предоставят больше функций, так что следите за новостями!
Если вы столкнетесь с проблемами производительности при использовании компилятора Kotlin K2, пожалуйста, сообщите об этом в нашем трекере ошибок.
Язык
Kotlin 1.7.0 вводит поддержку реализации делегированием и новый оператор подчёркивания для аргументов типов. Он также стабилизирует несколько языковых функций, представленных в предварительных версиях предыдущих релизов:
Разрешение реализации делегированием к встроенному значению встроенного класса
Если вы хотите создать лёгкую обёртку для значения или экземпляра класса, необходимо вручную реализовать все методы интерфейса. Реализация делегированием решает эту проблему, но до версии 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.
Стабильные требования к opt-in
Требования к opt-in теперь стабильны и не требуют дополнительной конфигурации компилятора.
До версии 1.7.0 сама функция opt-in требовала аргумент -opt-in=kotlin.RequiresOptIn для предотвращения предупреждения. Теперь это больше не требуется; однако, вы по-прежнему можете использовать параметр компилятора -opt-in для opt-in к другим аннотациям, глобальным.
Стабильные типы, определенно не допускающие null
В Kotlin 1.7.0 типы, определенно не допускающие null, были повышены до стабильного уровня. Они обеспечивают лучшую совместимость при расширении обобщенных Java-классов и интерфейсов.
Вы можете отметить обобщенный параметр типа как определенно не допускающий null в месте использования с новым синтаксисом T & Any. Синтаксическая форма взята из обозначения для пересечений типов и теперь ограничена параметром типа с необязательными верхними границами слева от & и не допускающим null 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 и добавлен новый параметр компилятора. Кроме того, вызовы конструкторов функциональных интерфейсов стали стабильными. Обратите внимание, что с версии 1.7.0, значение по умолчанию для целевой версии компиляции Kotlin/JVM равно 1.8.
Оптимизации производительности компилятора
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 управляет целевой версией байткода и ограничивает API JDK в пути к классам заданной версией Java. Например, kotlinc -Xjdk-release=1.8 не позволит ссылаться на java.lang.Module, даже если JDK в зависимостях имеет версию 9 или выше.
Пожалуйста, оставьте свои отзывы на этой странице YouTrack.
Стабильные вызовы конструкторов функциональных интерфейсов
Вызовы конструкторов функциональных интерфейсов теперь являются стабильными. Узнайте, как перейти от интерфейса с конструктором-функцией к функциональному интерфейсу, используя вызовы конструкторов.
Пожалуйста, сообщите о любых найденных проблемах на YouTrack.
Удалена целевая версия JVM 1.6
Значение по умолчанию целевой версии для компиляций Kotlin/JVM составляет 1.8. Целевая версия 1.6 была удалена.
Пожалуйста, перейдите на целевую версию JVM 1.8 или выше. Информация о обновлении целевой версии JVM:
Kotlin/Native
В Kotlin 1.7.0 внесены изменения в межъязыковую совместимость с Objective-C и Swift, и стабилизированы функции, представленные в предыдущих выпусках. Также добавлены улучшения производительности нового менеджера памяти, а также другие обновления:
Взаимодействие со Swift async/await: возврат
VoidвместоKotlinUnitПереопределение URL для скачивания компилятора Kotlin/Native
Улучшения производительности нового менеджера памяти
Новый менеджер памяти всё ещё в стадии Альфа, но он приближается к стадии Стабильность. Этот релиз содержит значительные улучшения производительности нового менеджера памяти, особенно в сборке мусора (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 и других поддерживаемых платформ.
Поддержка автономных исполняемых файлов для 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. Это результат улучшенного взаимодействия со Swift's async/await. Эта функция была представлена в 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, например, для обработки зависимостей iOS в проектах Kotlin Multiplatform Mobile, вам нужно было установить как менеджер зависимостей CocoaPods, так и плагин cocoapods-generate.
Теперь настройка интеграции 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
Kotlin/JS
Kotlin/JS получает дальнейшие улучшения бэкенда компилятора JS IR, а также другие обновления, которые могут улучшить ваш опыт разработки:
Поддержка старых браузеров с помощью полифиллов в бэкенде IR
Указание переменных окружения для 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 значения
Отметки времени на основе встроенных классов для стандартного источника времени
Новые экспериментальные расширяющие функции для Java Optional
Функции min() и max() для коллекций возвращают не-null значения
В Kotlin 1.4.0 мы переименовали функции min() и max() для коллекций на minOrNull() и maxOrNull(). Эти новые имена лучше отражают их поведение — возвращение null, если коллекция-получатель пустая. Это также помогло выровнять поведение функций с соглашениями об именовании, используемыми во всем API коллекций Kotlin.
То же самое относилось к minBy(), maxBy(), minWith(), и maxWith(), которые в Kotlin 1.4.0 получили синонимы с *OrNull(). Более старые функции, затронутые этим изменением, постепенно устарели.
Kotlin 1.7.0 повторно вводит оригинальные имена функций, но с типом возвращаемого значения non-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 вызовов.
Отметки времени на основе встроенных классов для стандартного источника времени
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()
}
Новые экспериментальные расширяющие функции для 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
Новая статистика сгенерированных файлов каждым процессором аннотаций в kapt
Устаревание системной переменной kotlin.compiler.execution.strategy
Новый подход к инкрементальной компиляции
В Kotlin 1.7.0 мы переработали инкрементальную компиляцию для межмодульных изменений. Теперь инкрементальная компиляция также поддерживает изменения, внесенные внутри зависимых модулей, не написанных на Kotlin, и совместима с кешем Gradle сборки. Также улучшена поддержка избегания компиляции.
Мы ожидаем, что наибольшая выгода от нового подхода будет наблюдаться при использовании кеша сборки или при частых изменениях в не-Kotlin модулях Gradle. Наши тесты для проекта Kotlin на модуле kotlin-gradle-plugin показывают улучшение более чем на 80% после попадания в кэш.
Чтобы попробовать этот новый подход, установите следующую опцию в вашем gradle.properties.
kotlin.incremental.useClasspathSnapshot=true
Узнайте, как реализован новый подход к инкрементальной компиляции внутри, в этой статье блога.
Мы планируем стабилизировать эту технологию и добавить поддержку других бэкендов (например, JS) и систем сборки. Будем признательны за ваши отчеты в YouTrack об обнаруженных проблемах или необычном поведении в этой схеме компиляции. Спасибо!
Команда Kotlin выражает большую благодарность Ивану Гавриловичу, Хунгу Нгуену, Седрику Шанпо и другим внешним участникам за помощь.
Отчеты о сборке для задач компилятора Kotlin
Kotlin 1.7.0 вводит отчеты о сборке, которые помогают отслеживать производительность компилятора. Отчеты содержат продолжительности различных фаз компиляции и причины, по которым компиляция не была инкрементальной.
Отчеты о сборке полезны, когда вы хотите исследовать проблемы с задачами компилятора, например:
Когда сборка Gradle занимает слишком много времени, и вы хотите понять причину низкой производительности.
Когда время компиляции одного и того же проекта различается, иногда занимая секунды, иногда — минуты.
Чтобы включить отчеты о сборке, укажите, куда сохранить вывод отчета о сборке в gradle.properties.
kotlin.build.report.output=file
Доступны следующие значения (и их комбинации):
fileсохраняет отчеты о сборке в локальном файле.-
build_scanсохраняет отчеты о сборке в разделеcustom valuesотчёта о сборке сборки. 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.3gradle70для версий Gradle 7.0 и выше
В будущих версиях Kotlin мы можем добавить больше вариантов.
Чтобы проверить, какой вариант использует ваш сборка, включите уровень --info уровня логов и найдите в выводе строку, начинающуюся с Using Kotlin Gradle plugin, например, Using Kotlin Gradle plugin main variant.
Оставьте свой отзыв на этом тикете 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все еще имеет устаревшее свойство Kotlinclasspath, которое будет удалено в будущих версиях.Задачи компиляции по-прежнему реализуют интерфейс
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
Статистика будет отображаться в логах с уровнем 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.
Удаление устаревших параметров, методов и плагинов
Удаление метода 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.
Удаление устаревших плагинов
В Kotlin 1.4.0 плагины kotlin2js и kotlin-dce-plugin были устаревшими, и они были удалены в этом релизе. Вместо kotlin2js, используйте новый плагин org.jetbrains.kotlin.js. Удаление мёртвого кода (DCE) работает, когда плагин Gradle Kotlin/JS правильно настроен.
В Kotlin 1.6.0 мы изменили уровень устаревания класса KotlinGradleSubplugin на ERROR. Разработчики использовали этот класс для написания плагинов компилятора. В этом выпуске этот класс был удалён. Используйте класс KotlinCompilerPluginSupportPlugin вместо него.
Удаление устаревшего параметра DSL для корутин и свойства
Мы удалили устаревший параметр DSL kotlin.experimental.coroutines Gradle и свойство 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.
Новый компилятор командной строки доступен для скачивания на странице релизов на 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.
© 2010–2023 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/whatsnew17.html