Spec-Zone.ru › Kotlin 2

Что нового в Kotlin 1.6.20

Выпущена: 4 апреля 2022 г.

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

Краткий обзор изменений также можно посмотреть в этом видео:

Информацию о цикле выпуска Kotlin см. в разделе Процесс выпуска Kotlin.

Язык

В Kotlin 1.6.20 можно попробовать две новые возможности языка:

  • Прототип контекстных получателей для Kotlin/JVM

  • Определённо не допускающие null типы

Прототип контекстных получателей для Kotlin/JVM

Эта возможность является прототипом, доступным только для Kotlin/JVM. Если включить -Xcontext-receivers, компилятор будет создавать предварительные бинарные файлы, которые нельзя использовать в производственном коде. Используйте контекстные получатели только в учебных проектах. Мы будем благодарны за ваши отзывы в YouTrack.

В Kotlin 1.6.20 вы больше не ограничены одним получателем. Если вам нужно больше, вы можете сделать функции, свойства и классы зависимыми от контекста (или контекстными), добавив контекстные получатели в их объявления. Контекстное объявление выполняет следующие действия:

  • Оно требует, чтобы все объявленные контекстные получатели присутствовали в области видимости вызывающего кода в качестве неявных получателей.

  • Оно добавляет объявленные контекстные получатели в область видимости тела в качестве неявных получателей.

interface LoggingContext {
    val log: Logger // This context provides a reference to a logger 
}

context(LoggingContext)
fun startBusinessOperation() {
    // You can access the log property since LoggingContext is an implicit receiver
    log.info("Operation has started")
}

fun test(loggingContext: LoggingContext) {
    with(loggingContext) {
        // You need to have LoggingContext in a scope as an implicit receiver
        // to call startBusinessOperation()
        startBusinessOperation()
    }
}

Чтобы включить контекстные получатели в проекте, используйте параметр компилятора -Xcontext-receivers. Подробное описание этой возможности и её синтаксиса см. в KEEP.

Обратите внимание, что эта реализация является прототипом:

  • Если включить -Xcontext-receivers, компилятор будет создавать предварительные бинарные файлы, которые нельзя использовать в производственном коде

  • На данный момент поддержка контекстных получателей в IDE минимальна

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

Определённо не допускающие null типы

Определённо не допускающие null типы находятся в статусе Beta. Они почти стабильны, но в будущем могут потребоваться шаги по миграции. Мы постараемся свести к минимуму необходимые изменения.

Для обеспечения лучшей совместимости при расширении обобщённых классов и интерфейсов Java в Kotlin 1.6.20 можно пометить параметр обобщённого типа как определённо не допускающий null в месте использования с помощью нового синтаксиса T & Any. Эта синтаксическая форма основана на обозначении типов-пересечений и теперь применяется только к параметру типа с допускаемыми null верхними границами слева от & и не допускающим null Any справа:

fun <T> elvisLike(x: T, y: T & Any): T & Any = x ?: y

fun main() {
    // OK
    elvisLike<String>("", "").length
    // Error: 'null' cannot be a value of a non-null type
    elvisLike<String>("", null).length

    // OK
    elvisLike<String?>(null, "").length
    // Error: 'null' cannot be a value of a non-null type
    elvisLike<String?>(null, null).length
}

Чтобы включить эту возможность, установите версию языка 1.7:

kotlin {
    sourceSets.all {
        languageSettings.apply {
            languageVersion = "1.7"
        }
    }
}
kotlin {
    sourceSets.all {
        languageSettings {
            languageVersion = '1.7'
        }
    }
}

Подробнее об определённо не допускающих null типах см. в KEEP.

Kotlin/JVM

В Kotlin 1.6.20 появились:

  • Улучшения совместимости методов по умолчанию в интерфейсах JVM: новая аннотация @JvmDefaultWithCompatibility для интерфейсов и изменения совместимости в режимах -Xjvm-default

  • Поддержка параллельной компиляции одного модуля в бэкенде JVM

  • Поддержка ссылок на конструкторы функциональных интерфейсов

Новая аннотация @JvmDefaultWithCompatibility для интерфейсов

В Kotlin 1.6.20 появилась новая аннотация @JvmDefaultWithCompatibility: используйте её вместе с параметром компилятора -Xjvm-default=all, чтобы создавать метод по умолчанию в интерфейсе JVM для любого неабстрактного члена любого интерфейса Kotlin.

Если существуют клиенты, использующие ваши интерфейсы Kotlin, скомпилированные без параметра -Xjvm-default=all, их бинарные файлы могут быть несовместимы с кодом, скомпилированным с этим параметром. До Kotlin 1.6.20, чтобы избежать этой проблемы совместимости, рекомендуемым подходом было использовать режим -Xjvm-default=all-compatibility, а также аннотацию @JvmDefaultWithoutCompatibility для интерфейсов, которым не требовался такой тип совместимости.

У этого подхода были некоторые недостатки:

  • Можно было легко забыть добавить аннотацию при добавлении нового интерфейса.

  • Обычно в непубличных частях кода интерфейсов больше, чем в публичном API, поэтому в итоге эту аннотацию приходилось использовать во многих местах кода.

Теперь можно использовать режим -Xjvm-default=all и помечать интерфейсы аннотацией @JvmDefaultWithCompatibility. Это позволяет один раз добавить эту аннотацию ко всем интерфейсам публичного API, а для нового непубличного кода аннотации не понадобятся.

Оставьте отзыв о новой аннотации в этой задаче YouTrack.

Изменения совместимости в режимах -Xjvm-default

В Kotlin 1.6.20 добавлена возможность компилировать модули в режиме по умолчанию (параметр компилятора -Xjvm-default=disable) с модулями, скомпилированными в режимах -Xjvm-default=all или -Xjvm-default=all-compatibility. Как и прежде, компиляция также будет успешной, если все модули используют режимы -Xjvm-default=all или -Xjvm-default=all-compatibility. Оставить отзыв можно в этой задаче YouTrack.

В Kotlin 1.6.20 режимы compatibility и enable параметра компилятора -Xjvm-default объявлены устаревшими. В описания других режимов внесены изменения, касающиеся совместимости, но общая логика осталась прежней. Ознакомьтесь с обновлёнными описаниями.

Дополнительную информацию о методах по умолчанию при взаимодействии с Java см. в документации по взаимодействию и этой записи в блоге.

Поддержка параллельной компиляции одного модуля в бэкенде JVM

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

Мы продолжаем работу над тем, чтобы ускорить компиляцию в новом бэкенде JVM IR. В Kotlin 1.6.20 мы добавили экспериментальный режим бэкенда JVM IR, который компилирует все файлы модуля параллельно. Параллельная компиляция может сократить общее время компиляции до 15%.

Включите экспериментальный режим параллельного бэкенда с помощью параметра компилятора -Xbackend-threads. Для этого параметра используйте следующие аргументы:

  • N — количество потоков, которое вы хотите использовать. Оно не должно превышать количество ядер процессора; иначе параллелизация теряет эффективность из-за переключения контекста между потоками

  • 0 — использовать отдельный поток для каждого ядра процессора

Gradle может выполнять задачи параллельно, но такой вид параллелизации не сильно помогает, если с точки зрения Gradle проект (или его значительная часть) представляет собой одну большую задачу. Если у вас очень большой монолитный модуль, используйте параллельную компиляцию, чтобы ускорить её. Если проект состоит из множества небольших модулей и сборка распараллелена средствами Gradle, дополнительный уровень параллелизации может снизить производительность из-за переключения контекста.

У параллельной компиляции есть некоторые ограничения:

  • Она не работает с kapt, так как kapt отключает бэкенд IR

  • По своей конструкции она требует больше памяти кучи JVM. Объём памяти кучи пропорционален количеству потоков

Поддержка ссылок на конструкторы функциональных интерфейсов

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

Поддержка ссылок на вызываемые объекты для конструкторов функциональных интерфейсов позволяет перейти с интерфейса с функцией-конструктором на функциональный интерфейс без нарушения совместимости исходного кода.

Рассмотрим следующий код:

interface Printer {
    fun print()
}

fun Printer(block: () -> Unit): Printer = object : Printer { override fun print() = block() }

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

fun interface Printer {
    fun print()
}

Его конструктор будет создан неявно, и любой код, использующий ссылку на функцию ::Printer, скомпилируется. Например:

documentsStorage.addPrinter(::Printer)

Сохраните бинарную совместимость, пометив устаревшую функцию Printer аннотацией @Deprecated с параметром DeprecationLevel.HIDDEN:

@Deprecated(message = "Your message about the deprecation", level = DeprecationLevel.HIDDEN)
fun Printer(...) {...}

Используйте параметр компилятора -XXLanguage:+KotlinFunInterfaceConstructorReference, чтобы включить эту возможность.

Kotlin/Native

В Kotlin/Native 1.6.20 продолжается разработка новых компонентов. Мы сделали ещё один шаг к единообразию работы с Kotlin на других платформах:

  • Обновление нового менеджера памяти

  • Параллельная реализация фазы очистки в новом менеджере памяти

  • Создание экземпляров классов аннотаций

  • Взаимодействие со Swift async/await: возврат Swift Void вместо KotlinUnit

  • Более информативные трассировки стека с libbacktrace

  • Поддержка автономных исполняемых файлов Android

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

  • Улучшена обработка ошибок при импорте модулей cinterop

  • Поддержка библиотек Xcode 13

Обновление нового менеджера памяти

Новый менеджер памяти Kotlin/Native находится на стадии альфа-версии. Он может измениться несовместимым образом и потребовать ручной миграции в будущем. Будем признательны за ваши отзывы о нём в YouTrack.

В Kotlin 1.6.20 можно попробовать альфа-версию нового менеджера памяти Kotlin/Native. Он устраняет различия между платформами JVM и Native, обеспечивая единообразный опыт разработки в мультиплатформенных проектах. Например, вам будет гораздо проще создавать новые кроссплатформенные мобильные приложения для Android и iOS.

Новый менеджер памяти Kotlin/Native снимает ограничения на совместное использование объектов между потоками. Он также предоставляет безопасные примитивы для параллельного программирования, не допускающие утечек памяти и не требующие особого управления или аннотаций.

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

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

Параллельная реализация фазы очистки в новом менеджере памяти

Если вы уже перешли на наш новый менеджер памяти, представленный в Kotlin 1.6, вы могли заметить значительное сокращение времени выполнения: наши тесты производительности показывают среднее улучшение на 35%. Начиная с версии 1.6.20 в новом менеджере памяти также доступна параллельная реализация фазы очистки. Это должно повысить производительность и сократить продолжительность пауз сборщика мусора.

Чтобы включить эту функцию для нового менеджера памяти Kotlin/Native, передайте следующий параметр компилятора:

-Xgc=cms 

Поделитесь отзывами о производительности нового менеджера памяти в этой задаче в YouTrack.

Создание экземпляров классов аннотаций

В Kotlin 1.6.0 создание экземпляров классов аннотаций стало стабильным для Kotlin/JVM и Kotlin/JS. В версии 1.6.20 добавлена поддержка Kotlin/Native.

Подробнее о создании экземпляров классов аннотаций.

Взаимодействие со Swift async/await: возврат Void вместо KotlinUnit

Взаимодействие с параллельными механизмами Swift async/await — экспериментальная функция. Она может быть удалена или изменена в любое время. Используйте её только для оценки. Будем признательны за ваши отзывы о ней в YouTrack.

Мы продолжили работу над экспериментальным взаимодействием со Swift async/await (доступно начиная со Swift 5.5). В Kotlin 1.6.20 изменился способ работы с функциями suspend с возвращаемым типом Unit.

Ранее такие функции представлялись в Swift как функции async, возвращающие KotlinUnit. Однако их правильный возвращаемый тип — Void, как и у несуспендируемых функций.

Чтобы не нарушать существующий код, мы добавляем свойство Gradle, которое позволяет компилятору преобразовывать suspend-функции, возвращающие Unit, в функции Swift async с возвращаемым типом Void:

# gradle.properties
kotlin.native.binary.unitSuspendFunctionObjCExport=proper

В будущих выпусках Kotlin мы планируем сделать это поведение стандартным.

Более информативные трассировки стека с libbacktrace

Использование libbacktrace для определения расположения исходного кода — экспериментальная функция. Она может быть удалена или изменена в любое время. Используйте её только для оценки. Будем признательны за ваши отзывы о ней в YouTrack.

Теперь Kotlin/Native может создавать подробные трассировки стека с указанием файлов и номеров строк, что упрощает отладку linux* (за исключением linuxMips32 и linuxMipsel32) и целевых платформ androidNative*.

Эта функция использует библиотеку libbacktrace. Посмотрите на пример ниже, чтобы увидеть разницу:

fun main() = bar()
fun bar() = baz()
inline fun baz() {
    error("")
}
  • До версии 1.6.20:

Uncaught Kotlin exception: kotlin.IllegalStateException: at 0 example.kexe 0x227190 kfun:kotlin.Throwable#<init>(kotlin.String?){} + 96 at 1 example.kexe 0x221e4c kfun:kotlin.Exception#<init>(kotlin.String?){} + 92 at 2 example.kexe 0x221f4c kfun:kotlin.RuntimeException#<init>(kotlin.String?){} + 92 at 3 example.kexe 0x22234c kfun:kotlin.IllegalStateException#<init>(kotlin.String?){} + 92 at 4 example.kexe 0x25d708 kfun:#bar(){} + 104 at 5 example.kexe 0x25d68c kfun:#main(){} + 12
  • Версия 1.6.20 с libbacktrace:

Uncaught Kotlin exception: kotlin.IllegalStateException: at 0 example.kexe 0x229550 kfun:kotlin.Throwable#<init>(kotlin.String?){} + 96 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Throwable.kt:24:37) at 1 example.kexe 0x22420c kfun:kotlin.Exception#<init>(kotlin.String?){} + 92 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:23:44) at 2 example.kexe 0x22430c kfun:kotlin.RuntimeException#<init>(kotlin.String?){} + 92 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:34:44) at 3 example.kexe 0x22470c kfun:kotlin.IllegalStateException#<init>(kotlin.String?){} + 92 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:70:44) at 4 example.kexe 0x25fac8 kfun:#bar(){} + 104 [inlined] (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/libraries/stdlib/src/kotlin/util/Preconditions.kt:143:56) at 5 example.kexe 0x25fac8 kfun:#bar(){} + 104 [inlined] (/private/tmp/backtrace/src/commonMain/kotlin/app.kt:4:5) at 6 example.kexe 0x25fac8 kfun:#bar(){} + 104 (/private/tmp/backtrace/src/commonMain/kotlin/app.kt:2:13) at 7 example.kexe 0x25fa4c kfun:#main(){} + 12 (/private/tmp/backtrace/src/commonMain/kotlin/app.kt:1:14)

На целевых платформах Apple, где трассировки стека уже содержали расположение файлов и номера строк, libbacktrace предоставляет более подробную информацию о вызовах встроенных функций:

  • До версии 1.6.20:

Uncaught Kotlin exception: kotlin.IllegalStateException: at 0 example.kexe 0x10a85a8f8 kfun:kotlin.Throwable#<init>(kotlin.String?){} + 88 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Throwable.kt:24:37) at 1 example.kexe 0x10a855846 kfun:kotlin.Exception#<init>(kotlin.String?){} + 86 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:23:44) at 2 example.kexe 0x10a855936 kfun:kotlin.RuntimeException#<init>(kotlin.String?){} + 86 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:34:44) at 3 example.kexe 0x10a855c86 kfun:kotlin.IllegalStateException#<init>(kotlin.String?){} + 86 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:70:44) at 4 example.kexe 0x10a8489a5 kfun:#bar(){} + 117 (/private/tmp/backtrace/src/commonMain/kotlin/app.kt:2:1) at 5 example.kexe 0x10a84891c kfun:#main(){} + 12 (/private/tmp/backtrace/src/commonMain/kotlin/app.kt:1:14) ...
  • Версия 1.6.20 с libbacktrace:

Uncaught Kotlin exception: kotlin.IllegalStateException: at 0 example.kexe 0x10669bc88 kfun:kotlin.Throwable#<init>(kotlin.String?){} + 88 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Throwable.kt:24:37) at 1 example.kexe 0x106696bd6 kfun:kotlin.Exception#<init>(kotlin.String?){} + 86 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:23:44) at 2 example.kexe 0x106696cc6 kfun:kotlin.RuntimeException#<init>(kotlin.String?){} + 86 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:34:44) at 3 example.kexe 0x106697016 kfun:kotlin.IllegalStateException#<init>(kotlin.String?){} + 86 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:70:44) at 4 example.kexe 0x106689d35 kfun:#bar(){} + 117 [inlined] (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/libraries/stdlib/src/kotlin/util/Preconditions.kt:143:56) >> at 5 example.kexe 0x106689d35 kfun:#bar(){} + 117 [inlined] (/private/tmp/backtrace/src/commonMain/kotlin/app.kt:4:5) at 6 example.kexe 0x106689d35 kfun:#bar(){} + 117 (/private/tmp/backtrace/src/commonMain/kotlin/app.kt:2:13) at 7 example.kexe 0x106689cac kfun:#main(){} + 12 (/private/tmp/backtrace/src/commonMain/kotlin/app.kt:1:14) ...

Чтобы получать более информативные трассировки стека с libbacktrace, добавьте следующую строку в gradle.properties:

# gradle.properties
kotlin.native.binary.sourceInfoType=libbacktrace

Расскажите, насколько удобно вам отлаживать Kotlin/Native с libbacktrace, в этой задаче в YouTrack.

Поддержка автономных исполняемых файлов Android

Раньше Android Native исполняемые файлы в Kotlin/Native на самом деле не были исполняемыми файлами, а представляли собой разделяемые библиотеки, которые можно было использовать как NativeActivity. Теперь можно создавать стандартные исполняемые файлы для целевых платформ Android Native.

Для этого настройте блок executable целевой платформы androidNative в части build.gradle(.kts) вашего проекта. Добавьте следующий параметр для двоичного файла:

kotlin {
    androidNativeX64("android") {
        binaries {
            executable {
                binaryOptions["androidProgramType"] = "standalone"
            }
        }
    }
}

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

binaryOptions["androidProgramType"] = "nativeActivity"

Благодарим Mattia Iavarone за реализацию!

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

Мы активно работаем над Kotlin/Native, чтобы ускорить процесс компиляции и улучшить ваш опыт разработки.

В Kotlin 1.6.20 появились обновления производительности и исправления ошибок, влияющие на LLVM IR, генерируемый Kotlin. Согласно результатам тестирования наших внутренних проектов, среднее повышение производительности составило:

  • сокращение времени выполнения на 15%

  • сокращение размера кода на 20% как для выпускных, так и для отладочных двоичных файлов

  • сокращение времени компиляции выпускных двоичных файлов на 26%

Эти изменения также позволяют сократить время компиляции отладочного двоичного файла на 10% в крупном внутреннем проекте.

Для этого мы реализовали статическую инициализацию некоторых синтетических объектов, созданных компилятором, улучшили структуру LLVM IR для каждой функции и оптимизировали кэши компилятора.

Улучшена обработка ошибок при импорте модулей cinterop

В этом выпуске улучшена обработка ошибок, возникающих при импорте модуля Objective-C с помощью инструмента cinterop (как это обычно происходит с CocoaPods pod). Раньше при возникновении ошибки во время работы с модулем Objective-C (например, при ошибке компиляции заголовка) отображалось малоинформативное сообщение, например fatal error: could not build module $name. Мы доработали эту часть инструмента cinterop, поэтому теперь сообщение об ошибке содержит подробное описание.

Поддержка библиотек Xcode 13

В этом выпуске обеспечена полная поддержка библиотек, поставляемых с Xcode 13. Их можно использовать в любом месте кода Kotlin.

Kotlin Multiplatform

В версии 1.6.20 появились следующие важные обновления Kotlin Multiplatform:

  • Иерархическая структура теперь используется по умолчанию во всех новых мультиплатформенных проектах

  • В плагин Kotlin CocoaPods Gradle добавлено несколько полезных функций для интеграции с CocoaPods

Поддержка иерархической структуры в мультиплатформенных проектах

В Kotlin 1.6.20 поддержка иерархической структуры включена по умолчанию. С момента её появления в Kotlin 1.4.0 мы значительно улучшили frontend и обеспечили стабильный импорт в IDE.

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

Теперь можно совместно использовать исходный код для нескольких схожих целевых платформ Native, использующих многие общие компоненты логики и сторонние API. Технология автоматически добавит правильные зависимости и определит точный набор API, доступный в общем коде. Это избавляет от сложной настройки сборки и обходных решений для поддержки совместного использования наборов исходного кода между целевыми платформами Native в IDE. Кроме того, это помогает предотвратить небезопасное использование API, предназначенных для другой целевой платформы.

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

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

Удобное совместное использование кода в проекте

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

Благодаря поддержке иерархической структуры проекта теперь это можно сделать без дополнительных настроек. В новой структуре наборы исходного кода образуют иерархию. Можно использовать специфичные для платформы языковые возможности и зависимости, доступные для каждой целевой платформы, для которой компилируется данный набор исходного кода.

Например, рассмотрим типичный мультиплатформенный проект с двумя целевыми платформами — iosArm64 и iosX64 для устройств и симуляторов iOS. Инструменты Kotlin распознают, что обе целевые платформы содержат одну и ту же функцию, и позволяют обращаться к ней из промежуточного набора исходного кода iosMain.

iOS hierarchy example

Инструментарий Kotlin автоматически добавляет правильные зависимости, например Kotlin/Native stdlib или библиотеки Native. Кроме того, инструменты Kotlin постараются точно определить набор API, доступный в общем коде. Это помогает избежать, например, использования специфичной для macOS функции в коде, предназначенном для Windows.

Больше возможностей для авторов библиотек

При публикации мультиплатформенной библиотеки API её промежуточных наборов исходного кода теперь публикуется вместе с ней и становится доступным пользователям. Инструментарий Kotlin автоматически определит API, доступный в наборе исходного кода проекта-потребителя, и проследит за тем, чтобы не использовались небезопасные возможности, например API для JVM в коде JS. Подробнее о совместном использовании кода в библиотеках.

Настройка

Начиная с Kotlin 1.6.20, во всех новых мультиплатформенных проектах будет использоваться иерархическая структура. Дополнительная настройка не требуется.

  • Если вы уже включили её вручную, можно удалить устаревшие параметры из gradle.properties:

    # gradle.properties
    kotlin.mpp.enableGranularSourceSetsMetadata=true
    kotlin.native.enableDependencyPropagation=false // or 'true', depending on your previous setup
    
  • Для Kotlin 1.6.20 рекомендуем использовать Android Studio 2021.1.1 (Bumblebee) или более позднюю версию, чтобы получить наилучший результат.

  • Вы также можете отказаться от этой возможности. Чтобы отключить поддержку иерархической структуры, задайте следующие параметры в gradle.properties:

    # gradle.properties
    kotlin.mpp.hierarchicalStructureSupport=false
    

Оставьте отзыв

Это важное изменение для всей экосистемы. Будем признательны за ваши отзывы — они помогут сделать её ещё лучше.

Попробуйте новую возможность и сообщите о возникших трудностях в наш трекер задач.

Плагин Kotlin CocoaPods Gradle

В Kotlin 1.6.20 появились следующие возможности, упрощающие интеграцию с CocoaPods:

  • Теперь плагин CocoaPods включает задачи для сборки XCFramework со всеми зарегистрированными целевыми платформами и создания файла Podspec. Это пригодится, если вы не хотите интегрироваться непосредственно с Xcode, но хотите собрать артефакты и развернуть их в локальном репозитории CocoaPods.

    Подробнее о сборке XCFramework.

  • Если вы используете в своих проектах интеграцию с CocoaPods, то, вероятно, уже задаёте требуемую версию Pod для всего проекта Gradle. Теперь у вас больше вариантов:

    • Указать версию Pod непосредственно в блоке cocoapods

    • Продолжить использовать версию проекта Gradle

    Если ни одно из этих свойств не настроено, возникнет ошибка.

  • Теперь имя CocoaPod можно настроить в блоке cocoapods, не меняя имя всего проекта Gradle.

  • В плагине CocoaPods появилось новое свойство extraSpecAttributes, которое позволяет настраивать свойства файла Podspec, ранее заданные жёстко, например libraries или vendored_frameworks.

kotlin {
    cocoapods {
        version = "1.0"
        name = "MyCocoaPod"
        extraSpecAttributes["social_media_url"] = 'https://twitter.com/kotlin'
        extraSpecAttributes["vendored_frameworks"] = 'CustomFramework.xcframework'
        extraSpecAttributes["libraries"] = 'xml'
    }
}

См. полный справочник DSL плагина Kotlin CocoaPods Gradle.

Kotlin/JS

Улучшения Kotlin/JS в версии 1.6.20 в основном затрагивают компилятор IR:

  • Инкрементальная компиляция для бинарных файлов разработки (IR)

  • Ленивая инициализация свойств верхнего уровня по умолчанию (IR)

  • Отдельные JS-файлы для модулей проекта по умолчанию (IR)

  • Оптимизация класса Char

  • Улучшения экспорта (бэкенды IR и legacy)

  • Гарантии @AfterTest для асинхронных тестов

Инкрементальная компиляция бинарных файлов разработки с компилятором IR

Чтобы сделать разработку на Kotlin/JS с компилятором IR эффективнее, мы добавляем новый режим инкрементальной компиляции.

При сборке бинарных файлов разработки с помощью задачи Gradle compileDevelopmentExecutableKotlinJs в этом режиме компилятор кэширует результаты предыдущих компиляций на уровне модуля. При последующих компиляциях он использует кэшированные результаты для неизменённых исходных файлов, благодаря чему сборка выполняется быстрее, особенно при небольших изменениях. Обратите внимание: это улучшение предназначено исключительно для процесса разработки (сокращения цикла «редактирование — сборка — отладка») и не влияет на сборку производственных артефактов.

Чтобы включить инкрементальную компиляцию бинарных файлов разработки, добавьте следующую строку в gradle.properties проекта:

# gradle.properties
kotlin.incremental.js.ir=true // false by default

В наших тестовых проектах новый режим ускорил инкрементальную компиляцию на 30%. Однако чистая сборка в этом режиме стала медленнее из-за необходимости создавать и заполнять кэши.

Расскажите, что вы думаете об использовании инкрементальной компиляции в своих проектах Kotlin/JS, в этой задаче YouTrack.

Ленивая инициализация свойств верхнего уровня по умолчанию с компилятором IR

В Kotlin 1.4.30 мы представили прототип ленивой инициализации свойств верхнего уровня в компиляторе JS IR. Поскольку при запуске приложения больше не нужно инициализировать все свойства, ленивая инициализация сокращает время запуска. Наши измерения показали ускорение примерно на 10% в реальном приложении Kotlin/JS.

Теперь, после доработки и надлежащего тестирования этого механизма, мы делаем ленивую инициализацию свойств верхнего уровня поведением по умолчанию в компиляторе IR.

// lazy initialization
val a = run {
    val result = // intensive computations
        println(result)
    result
} // run is executed upon the first usage of the variable

Если по какой-либо причине вам нужно инициализировать свойство без задержки (при запуске приложения), пометьте его аннотацией @EagerInitialization.

Отдельные JS-файлы для модулей проекта по умолчанию с компилятором IR

Ранее компилятор JS IR предоставлял возможность создавать отдельные файлы .js для модулей проекта. Это было альтернативой варианту по умолчанию — единому файлу .js для всего проекта. Такой файл может быть слишком большим и неудобным в использовании: каждый раз, когда вы хотите использовать функцию из своего проекта, вам приходится подключать весь JS-файл в качестве зависимости. Несколько файлов обеспечивают большую гибкость и уменьшают размер таких зависимостей. Эта возможность была доступна с параметром компилятора -Xir-per-module.

Начиная с версии 1.6.20 компилятор JS IR по умолчанию создаёт отдельные файлы .js для модулей проекта.

Компиляция проекта в один файл .js теперь доступна с помощью следующего свойства Gradle:

# gradle.properties
kotlin.js.ir.output.granularity=whole-program // `per-module` is the default

В предыдущих выпусках экспериментальный режим для каждого модуля (доступный с помощью флага -Xir-per-module=true) вызывал функции main() в каждом модуле. Это не соответствовало обычному режиму с одним файлом .js. Начиная с версии 1.6.20 функция main() в обоих случаях будет вызываться только в главном модуле. Если вам нужно выполнить код при загрузке модуля, можно использовать свойства верхнего уровня с аннотацией @EagerInitialization. См. раздел Ленивая инициализация свойств верхнего уровня по умолчанию (IR).

Оптимизация класса Char

Теперь класс Char обрабатывается компилятором Kotlin/JS без создания обёртки (как и inline-классы). Это ускоряет операции с символами в коде Kotlin/JS.

Помимо повышения производительности, это меняет способ экспорта Char в JavaScript: теперь он преобразуется в Number.

Улучшения экспорта и генерации объявлений TypeScript

В Kotlin 1.6.20 внесено несколько исправлений и улучшений в механизм экспорта (аннотация @JsExport), включая генерацию объявлений TypeScript (.d.ts). Мы добавили возможность экспортировать интерфейсы и перечисления, а также исправили поведение экспорта в некоторых особых случаях, о которых нам сообщали ранее. Подробнее см. в списке улучшений экспорта в YouTrack.

Подробнее об использовании кода Kotlin из JavaScript.

Гарантии @AfterTest для асинхронных тестов

В Kotlin 1.6.20 функции @AfterTest корректно работают с асинхронными тестами в Kotlin/JS. Если тип возвращаемого значения тестовой функции статически определён как Promise, компилятор теперь планирует выполнение функции @AfterTest в соответствующем обработчике then().

Безопасность

В Kotlin 1.6.20 добавлено несколько возможностей для повышения безопасности вашего кода:

  • Использование относительных путей в klib

  • Сохранение yarn.lock для проектов Kotlin/JS на Gradle

  • Установка зависимостей npm с параметром --ignore-scripts по умолчанию

Использование относительных путей в klib

Библиотека в формате klib содержит сериализованное представление IR исходных файлов, включая пути к ним, необходимые для создания корректной отладочной информации. До Kotlin 1.6.20 пути к файлам сохранялись как абсолютные. Поскольку автор библиотеки может не захотеть раскрывать абсолютные пути, в версии 1.6.20 появилась альтернативная возможность.

Если вы публикуете klib и хотите использовать в артефакте только относительные пути к исходным файлам, теперь можно передать параметр компилятора -Xklib-relative-path-base с одним или несколькими базовыми путями к исходным файлам:

tasks.withType(org.jetbrains.kotlin.gradle.dsl.KotlinCompile::class).configureEach {
    // $base is a base path of source files
    kotlinOptions.freeCompilerArgs += "-Xklib-relative-path-base=$base"
}
tasks.withType(org.jetbrains.kotlin.gradle.dsl.KotlinCompile).configureEach {
    kotlinOptions {
        // $base is a base path of source files
        freeCompilerArgs += "-Xklib-relative-path-base=$base"
    }
}

Сохранение yarn.lock для проектов Kotlin/JS на Gradle

Эта возможность была перенесена в Kotlin 1.6.10.

Плагин Kotlin/JS для Gradle теперь позволяет сохранять файл yarn.lock, благодаря чему можно фиксировать версии зависимостей npm для проекта без дополнительной настройки Gradle. Эта возможность меняет структуру проекта по умолчанию: в корень проекта добавляется автоматически созданный каталог kotlin-js-store, содержащий файл yarn.lock.

Мы настоятельно рекомендуем добавлять каталог kotlin-js-store и его содержимое в систему контроля версий. Добавление файлов блокировки в систему контроля версий — это рекомендуемая практика, поскольку она гарантирует, что приложение будет собираться с одним и тем же деревом зависимостей на всех компьютерах, будь то среды разработки на других компьютерах или службы CI/CD. Файлы блокировки также предотвращают незаметное обновление зависимостей npm при извлечении проекта на новый компьютер, что представляет собой угрозу безопасности.

Такие инструменты, как Dependabot, также могут анализировать файлы yarn.lock проектов Kotlin/JS и предупреждать вас, если какой-либо используемый пакет npm скомпрометирован.

При необходимости можно изменить имена каталога и файла блокировки в скрипте сборки:

rootProject.plugins.withType<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin> {
    rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().lockFileDirectory =
        project.rootDir.resolve("my-kotlin-js-store")
    rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().lockFileName = "my-yarn.lock"
}
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin) {
    rootProject.extensions.getByType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension).lockFileDirectory =
        file("my-kotlin-js-store")
    rootProject.extensions.getByType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension).lockFileName = 'my-yarn.lock'
}

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

Установка зависимостей npm с --ignore-scripts по умолчанию

Эта возможность была перенесена в Kotlin 1.6.10.

Теперь плагин Kotlin/JS для Gradle по умолчанию запрещает выполнение скриптов жизненного цикла во время установки зависимостей npm. Это изменение направлено на снижение вероятности выполнения вредоносного кода из скомпрометированных пакетов npm.

Чтобы вернуться к прежней конфигурации, можно явно разрешить выполнение скриптов жизненного цикла, добавив следующие строки в build.gradle(.kts):

rootProject.plugins.withType<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin> {
    rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().ignoreScripts = false
}
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin) {
    rootProject.extensions.getByType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension).ignoreScripts = false
}

Подробнее о зависимостях npm проекта Kotlin/JS на Gradle.

Gradle

В Kotlin 1.6.20 внесены следующие изменения в плагин Kotlin для Gradle:

  • Новые свойства kotlin.compiler.execution.strategy и compilerExecutionStrategy для задания стратегии выполнения компилятора Kotlin

  • Устаревание параметров kapt.use.worker.api, kotlin.experimental.coroutines и kotlin.coroutines

  • Удаление параметра сборки kotlin.parallel.tasks.in.project

Свойства для задания стратегии выполнения компилятора Kotlin

До Kotlin 1.6.20 для задания стратегии выполнения компилятора Kotlin использовалось системное свойство -Dkotlin.compiler.execution.strategy. В некоторых случаях это свойство могло быть неудобным. В Kotlin 1.6.20 появилось свойство Gradle с тем же именем, kotlin.compiler.execution.strategy, и свойство задачи компиляции compilerExecutionStrategy.

Системное свойство по-прежнему работает, но в будущих выпусках будет удалено.

Текущий приоритет свойств выглядит следующим образом:

  • Свойство задачи compilerExecutionStrategy имеет приоритет над системным свойством и свойством Gradle kotlin.compiler.execution.strategy.

  • Свойство Gradle имеет приоритет над системным свойством.

Для этих свойств можно задать одну из трёх стратегий выполнения компилятора:

Стратегия

Где выполняется компилятор Kotlin

Инкрементальная компиляция

Другие характеристики

Демон

В отдельном процессе демона

Да

Стратегия по умолчанию. Может использоваться совместно разными демонами Gradle

В процессе

В процессе демона Gradle

Нет

Может использовать кучу совместно с демоном Gradle

Вне процесса

В отдельном процессе для каждого вызова

Нет

—

Соответственно, допустимые значения для свойств kotlin.compiler.execution.strategy (как системного, так и Gradle) следующие:

  1. daemon (по умолчанию)

  2. in-process

  3. out-of-process

Используйте свойство Gradle kotlin.compiler.execution.strategy в gradle.properties:

# gradle.properties
kotlin.compiler.execution.strategy=out-of-process

Допустимые значения для свойства задачи compilerExecutionStrategy:

  1. org.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy.DAEMON (по умолчанию)

  2. org.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy.IN_PROCESS

  3. org.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy.OUT_OF_PROCESS

Используйте свойство задачи compilerExecutionStrategy в скрипте сборки build.gradle.kts:

import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy

// ...

tasks.withType<KotlinCompile>().configureEach {
    compilerExecutionStrategy.set(KotlinCompilerExecutionStrategy.IN_PROCESS)
}

Оставьте свой отзыв в этой задаче YouTrack.

Устаревание параметров сборки для kapt и корутин

В Kotlin 1.6.20 изменены уровни устаревания следующих свойств:

  • Мы объявили устаревшей возможность запускать kapt через демон Kotlin с помощью kapt.use.worker.api — теперь об этом выводится предупреждение в журнал Gradle. Начиная с версии 1.3.70 по умолчанию kapt использует рабочие процессы Gradle, и мы рекомендуем пользоваться именно этим способом.

    В будущих выпусках мы собираемся удалить параметр kapt.use.worker.api.

  • Мы объявили устаревшими параметр DSL Gradle kotlin.experimental.coroutines и свойство kotlin.coroutines, используемое в gradle.properties. Просто используйте приостанавливаемые функции или добавьте зависимость kotlinx.coroutines в файл build.gradle(.kts).

    Подробнее о корутинах см. в руководстве по корутинам.

Удаление параметра сборки kotlin.parallel.tasks.in.project

В Kotlin 1.5.20 мы объявили параметр сборки kotlin.parallel.tasks.in.project устаревшим. Этот параметр удалён в Kotlin 1.6.20.

В зависимости от проекта параллельная компиляция в демоне Kotlin может потребовать больше памяти. Чтобы уменьшить потребление памяти, увеличьте размер кучи для демона Kotlin.

Подробнее о поддерживаемых в настоящее время параметрах компилятора в плагине Kotlin для Gradle.

15 января 2026 г.
Руководство по совместимости с Kotlin 1.7.0Что нового в Kotlin 1.6.0

© 2010–2026 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/whatsnew1620.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API