Что нового в Kotlin 1.7.0
Дата выпуска: 9 июня 2022 года
Выпущен Kotlin 1.7.0. Он представляет собой альфа-версию нового компилятора Kotlin/JVM K2, стабилизирует языковые возможности и улучшает производительность для платформ JVM, JS и Native.
Вот список основных обновлений в этой версии:
Новый компилятор Kotlin K2 теперь в альфа-версии, и он предлагает значительные улучшения производительности. Он доступен только для проектов JVM, и ни один из плагинов компилятора, включая kapt, с ним не работает.
Новый подход к инкрементальной компиляции в Gradle. Инкрементальная компиляция теперь также поддерживает изменения, внесенные в зависимые модули, не написанные на Kotlin, и совместима с Gradle.
Мы стабилизировали аннотации требований для включения, типы, определенно не являющиеся нулевыми и вывод типов для билдеров.
Теперь есть оператор подчёркивания для аргументов типа. Вы можете использовать его для автоматического вывода типа аргумента при указании других типов.
В этом релизе появилась возможность реализации через делегирование к встроенному значению inline-класса. Теперь вы можете создавать лёгкие обёртки, которые в большинстве случаев не занимают память.
Вы также можете найти краткий обзор изменений в этом видео:
Новый компилятор 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 вводит поддержку реализации через делегирование и новый оператор подчёркивания для аргументов типа. Также в нём стабилизированы несколько языковых функций, представленных в предыдущих релизах в виде предварительных версий:
Реализация через делегирование к встроенному значению 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.
Стабилизированы требования для включения
Требования для включения теперь стабильны и не требуют дополнительной настройки компилятора.
До версии 1.7.0 само включение этой возможности требовало аргумент -opt-in=kotlin.RequiresOptIn, чтобы избежать предупреждения. Теперь это больше не требуется; однако, вы по-прежнему можете использовать аргумент компилятора -opt-in для включения других аннотаций, модульные.
Стабилизированы типы, определенно не являющиеся нулевыми
В Kotlin 1.7.0 типы, определенно не являющиеся нулевыми, переведены в стабильное состояние. Они обеспечивают лучшую совместимость при расширении универсальных Java-классов и интерфейсов.
Вы можете пометить универсальный параметр типа как определенно не являющийся нулевым в месте использования с новым синтаксисом T & Any. Синтаксическая форма заимствована из обозначения пересекающихся типов и теперь ограничена параметром типа с ограничением верхнего уровня, являющимся nullable, слева от & и не-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
}
Узнайте больше о типах, определенно не являющихся нулевыми, в этом документе 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 и стабилизирует функции, представленные в предыдущих выпусках. Также он улучшает производительность нового менеджера памяти и включает другие обновления:
Взаимодействие с Swift async/await: возврат
VoidвместоKotlinUnitПереопределение URL для скачивания компилятора Kotlin/Native
Улучшения производительности нового менеджера памяти
Новый менеджер памяти всё ещё на стадии Alpha, но он движется к состоянию Stable. Этот релиз предоставляет значительные улучшения производительности для нового менеджера памяти, особенно в сборке мусора (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 Optional
Функции 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 Optional
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отчета о сборке Gradle. 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-средства разработки, которые поддерживаются с 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–2023 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/whatsnew17.html