Что нового в Kotlin 1.7.0
Выпущена Kotlin 1.7.0. В этой версии представлена альфа-версия нового компилятора Kotlin/JVM K2, стабилизированы языковые возможности и повышена производительность платформ JVM, JS и Native.
Ниже перечислены основные обновления этой версии:
Новый компилятор Kotlin K2 теперь доступен в альфа-версии и обеспечивает значительный прирост производительности. Он доступен только для JVM, и с ним не работают плагины компилятора, в том числе kapt.
Новый подход к инкрементальной компиляции в Gradle. Теперь инкрементальная компиляция поддерживается и при изменениях в зависимых модулях, не написанных на Kotlin, и совместима с Gradle.
Стабилизированы аннотации требований opt-in, определённо не допускающие null типы и вывод типов в построителях.
Теперь для аргументов типов доступен оператор подчёркивания. С его помощью можно автоматически вывести тип аргумента, если указаны другие типы.
В этом выпуске добавлена возможность реализации через делегирование встроенному значению inline-класса. Теперь можно создавать лёгкие обёртки, которые в большинстве случаев не выделяют память.
Краткий обзор изменений также представлен в этом видео:
Новый компилятор Kotlin K2 для JVM в альфа-версии
В этом выпуске Kotlin представлена альфа-версия нового компилятора Kotlin K2. Новый компилятор призван ускорить разработку языковых возможностей, объединить все поддерживаемые Kotlin платформы, повысить производительность и предоставить API для расширений компилятора.
Мы уже опубликовали подробные объяснения о новом компиляторе и его преимуществах:
Важно отметить, что в альфа-версии нового компилятора 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 |
Вы можете проверить прирост производительности на своих проектах JVM и сравнить его с результатами старого компилятора. Чтобы включить компилятор Kotlin K2, используйте следующий параметр компилятора:
-Xuse-k2
Кроме того, компилятор K2 содержит ряд исправлений ошибок. Обратите внимание: проблемы из этого списка, у которых указано Состояние: открыта, на самом деле исправлены в K2.
В следующих выпусках Kotlin стабильность компилятора K2 будет повышена, а его возможности расширены. Следите за новостями!
Если вы столкнулись с проблемами производительности компилятора Kotlin K2, сообщите о них в нашем трекере задач.
Язык
В Kotlin 1.7.0 добавлена поддержка реализации через делегирование и новый оператор подчёркивания для аргументов типов. Кроме того, стабилизированы несколько языковых возможностей, представленных в качестве предварительных в предыдущих выпусках:
Реализация через делегирование встроенному значению 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.
Оптимизация производительности компилятора
В 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 или выше.
Оставьте отзыв в этой задаче YouTrack.
Стабильные ссылки на конструкторы функциональных интерфейсов
Ссылки на вызываемые объекты для конструкторов функциональных интерфейсов теперь имеют статус Stable. Узнайте, как перейти от интерфейса с функцией-конструктором к функциональному интерфейсу, используя ссылки на вызываемые объекты.
Сообщайте о найденных проблемах в 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Запрет необъявленных исключений при передаче через мосты Objective-C
Повышение производительности нового менеджера памяти
Новый менеджер памяти всё ещё находится в статусе 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 и других поддерживаемых платформ можно использовать одни и те же артефакты плагина компилятора.
Поддержка автономных исполняемых файлов 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
Kotlin/JS
В Kotlin/JS продолжается совершенствование бэкенда компилятора JS IR, а также появляются другие обновления, которые могут сделать разработку удобнее:
Повышение производительности нового бэкенда 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-значения
Метки времени на основе inline-классов для источника времени по умолчанию
Новые экспериментальные функции расширения для Java Optional
Функции коллекций 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()
}
Новые экспериментальные функции расширения для 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
Новая статистика по файлам, созданным каждым процессором аннотаций в kapt
Устаревание системного свойства kotlin.compiler.execution.strategy
Новый подход к инкрементальной компиляции
В Kotlin 1.7.0 мы переработали инкрементальную компиляцию при изменениях между модулями. Теперь инкрементальная компиляция также поддерживается при изменениях в зависимых модулях, не написанных на Kotlin, и совместима с кэшем сборки Gradle. Также была улучшена поддержка избегания компиляции.
Мы ожидаем, что наибольшую пользу от нового подхода вы получите, если используете кэш сборки или часто вносите изменения в модули Gradle, не написанные на Kotlin. Наши тесты для проекта 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 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по-прежнему есть устаревшее свойство 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. Подробнее см. на странице «Стратегия выполнения компилятора».
Удаление устаревших параметров, методов и плагинов
Удаление метода 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.
Удаление устаревших плагинов
В Kotlin 1.4.0 плагины kotlin2js и kotlin-dce-plugin были объявлены устаревшими, а в этом выпуске удалены. Вместо kotlin2js используйте новый плагин org.jetbrains.kotlin.js. Удаление мёртвого кода (DCE) работает при правильной настройке плагина Kotlin/JS Gradle.
В Kotlin 1.6.0 мы изменили уровень устаревания класса KotlinGradleSubplugin на ERROR. Разработчики использовали этот класс для написания плагинов компилятора. В этом выпуске этот класс удалён. Вместо него используйте класс KotlinCompilerPluginSupportPlugin.
Удаление устаревшего параметра 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.
Новый компилятор командной строки можно скачать на странице выпуска на 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–2026 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/whatsnew17.html