Что нового в 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 для расширений компилятора.
Мы уже опубликовали некоторые подробные объяснения нашего нового компилятора и его преимуществ:
Важно отметить, что в альфа-версии нового компилятора 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 раза |
Вы можете проверить прирост производительности в своих проектах 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.
Стабильные требования к опциональному включению
Требования к опциональному включению теперь стабильны и не требуют дополнительной конфигурации компилятора.
До версии 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.
Оптимизации производительности компилятора
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 или выше.
Пожалуйста, оставьте свои отзывы на этом тикете YouTrack.
Стабильные вызываемые ссылки на конструкторы функциональных интерфейсов
Вызываемые ссылки на конструкторы функциональных интерфейсов теперь стабильны. Узнайте, как перейти от интерфейса с конструкторной функцией к функциональному интерфейсу, используя вызываемые ссылки.
Пожалуйста, сообщите о любых проблемах на YouTrack.
Удалена целевая версия JVM 1.6
Значение по умолчанию для целевой версии компиляции Kotlin/JVM — 1.8. Целевая версия 1.6 удалена.
Пожалуйста, перейдите к целевой версии JVM 1.8 или выше. Узнайте, как обновить целевую версию JVM для:
Kotlin/Native
Kotlin 1.7.0 включает изменения в межплатформенной работе с Objective-C и Swift, а также стабилизирует функции, представленные в предыдущих версиях. Он также обеспечивает улучшения производительности для нового менеджера памяти и другие обновления:
Улучшения производительности нового менеджера памяти
Новый менеджер памяти всё ещё в стадии Альфа, но приближается к Стабильному состоянию. Этот выпуск обеспечивает значительные улучшения производительности для нового менеджера памяти, особенно в сборке мусора (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. Это результат улучшенного взаимодействия с 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
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 Optionals
Функции 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()
}
Новые экспериментальные расширяющие функции для 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скана сборки build scan. 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.2gradle70для версий 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) работает, когда плагин 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–2022 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/whatsnew17.html