Kotlin Releases

Вышел Kotlin 1.4.20

Read this post in other languages:
English, Français, 日本語, 한국어, Português do Brasil, Español

В свежей версии Kotlin мы подготовили для вас несколько экспериментальных функций. Команда Kotlin всегда открыта к обратной связи, и мы очень ждем ваших отзывов о новых возможностях языка. Пробуйте и делитесь своим мнением в нашем Slack-канале (получить приглашение) или в YouTrack.

Publication de Kotlin 1.4.20

Вот несколько основных нововведений:

  • Поддержка новых возможностей JVM, таких как конкатенация строк с помощью invokedynamic.
  • Улучшенная обработка исключений в KMM-проектах.
  • Расширения пути к JDK: Path(“dir”) / “file.txt”.

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

Подробное описание нововведений вы также найдете на странице What’s New в документации, а полный список — в журнале изменений.

Как всегда, хотим сказать спасибо всем внешним контрибьюторам, которые помогли нам выпустить эту версию.

А теперь обо всем подробнее.

Kotlin/JVM

Что касается JVM, мы поддержали JVM 15 и в основном сосредоточились на повышении производительности, улучшении существующей функциональности и исправлении ошибок.

Конкатенация строк через invokedynamic

Начиная с Java 9, конкатенация строк в JVM выполняется с помощью динамического вызова методов (инструкция invokedynamic в байт-коде). Такая реализация работает быстрее, чем предыдущий вариант, потребляет меньше памяти и оставляет место для будущих оптимизаций, не требуя изменения байт-кода.

Мы начали реализовывать этот механизм в Kotlin для повышения производительности, и теперь можно компилировать конкатенации строк в динамические вызовы для JVM 9+.

Пока что эта функция является экспериментальной и охватывает следующие случаи:

  • String.plus с использованием оператора (a + b), явной (a.plus(b)) или ссылочной ((a::plus)(b)) формы.
  • toString на inline- и data-классах.
  • Строковые шаблоны, за исключением тех, в которых есть один непостоянный аргумент (см. KT-42457).

Чтобы включить конкатенацию строк через invokedynamic, добавьте флаг -Xstring-concat с одним из следующих значений:

  • indy-with-constants для выполнения конкатенации строк с StringConcatFactory.makeConcatWithConstants() (планируется, что это будет реализация по умолчанию для JVM 9+ в версии 1.5).
  • indy для выполнения конкатенации строк с StringConcatFactory.makeConcat().
  • inline, чтобы вернуться к классической конкатенации через StringBuilder.append().

Kotlin/JS

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

Изменения в Gradle DSL

В Kotlin/JS Gradle DSL вас ждут несколько обновлений, которые упростят настройку проекта и персонализацию: корректировки конфигурации веб-пакета, модификации автоматически сгенерированного файла package.json и улучшенный контроль транзитивных зависимостей.

Единая точка настройки webpack

Kotlin 1.4.20 представляет новый блок конфигурации для таргета browser, который называется commonWebpackConfig. В нем можно управлять общими настройками централизованно, а не дублировать конфигурации в webpackTask, runTask и testTask.

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

Кастомизация package.json из Gradle

Файл package.json обычно указывает, как должен вести себя JavaScript-проект: в нем определены исполняемые скрипты, зависимости и многое другое. Для проектов Kotlin/JS этот файл создается автоматически во время сборки. Поскольку содержимое package.json меняется от случая к случаю, многие пользователи просили нас предоставить простой способ изменять этот файл.

Начиная с Kotlin 1.4.20, можно добавлять записи в файл проекта package.json из билд-скрипта Gradle. Чтобы добавить настраиваемые поля в package.json, используйте функцию customField в блоке компиляций packageJson:

При сборке проекта в файл конфигурации build/js/packages/projectName/package.json добавится следующий код:

"hello": {
  "one": 1,
  "two": 2
}

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

Выборочное разрешение зависимостей Yarn (экспериментальная функциональность)

При включении зависимостей из npm бывают случаи, когда необходимо хорошо контролировать их зависимости (транзитивные зависимости). Этому может быть много причин. Например, вам нужно применить важное обновление к одной из зависимостей библиотеки, которую вы используете. Или вы хотите откатить обновление транзитивной зависимости, которое нарушает работу вашего приложения. Выборочное разрешение зависимостей Yarn позволяет переопределить зависимости, указанные исходным автором, и вы можете продолжать разработку.

В Kotlin 1.4.20 появился экспериментальный способ настройки этой функции из билд-скрипта Gradle. Мы все еще работаем над интеграцией API с остальными опциями Kotlin/JS, но вы уже можете использовать эту функцию через YarnRootExtension внутри YarnPlugin. Чтобы повлиять на разрешенную версию пакета для вашего проекта, используйте функцию resolution. В ее аргументах укажите селектор имени пакета (как требует Yarn) и желаемую версию.

Пример конфигурации выборочного разрешения зависимостей в файле build.gradle.kts выглядит так:

Здесь все ваши npm-зависимости, требующие react, получат версию 16.0.0, а processor получит зависимость decamelize как версию 3.0.0. Кроме того, вы можете передавать вызовы include и exclude в блок resolution, что позволяет установить ограничения допустимых версий.

Отключение granular workspaces (экспериментальная функциональность)

Чтобы ускорить сборку, плагин Kotlin/JS Gradle устанавливает только те зависимости, которые необходимы для конкретной задачи Gradle. Например, пакет webpack-dev-server устанавливается только при выполнении одной из задач *Run, а не когда вы выполняете задачу assemble. Это позволяет избежать ненужных загрузок, однако может вызвать проблемы при параллельном запуске нескольких процессов Gradle. Когда требования зависимостей противоречат друг другу, две установки npm-пакетов могут вызвать ошибки.

Чтобы решить эту проблему, в Kotlin 1.4.20 добавлена новая экспериментальная опция для отключения этих так называемых гранулярных рабочих областей (granular workspaces). Как и экспериментальная поддержка выборочного разрешения зависимостей, эта функция сейчас доступна через YarnRootExtension, но, вероятно, впоследствии она будет более тесно интегрирована с остальной частью Kotlin/JS Gradle DSL. Чтобы воспользоваться ею, добавьте в файл build.gradle.kts следующий фрагмент:

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

Новые шаблоны в мастере проектов

Чтобы упростить создание проекта, мы добавили в мастер проектов новые настраиваемые шаблоны для приложений Kotlin/JS. Есть шаблоны как для браузерных приложений, так и для Node.js. Шаблоны позволяют легко настроить начальную конфигурацию и служат хорошей отправной точкой для вашего проекта. В шаблонах, помимо прочего, можно настроить использование нового IR-компилятора и поддержку дополнительных библиотек.

В Kotlin 1.4.20 доступно три шаблона:

  • Browser Application позволяет на базовом уровне настроить проект Kotlin/JS Gradle, который запускается в браузере.
  • React Application содержит все необходимое для начала создания React-приложения с использованием соответствующих kotlin-wrappers. Можно настроить интеграцию с таблицами стилей, компонентами навигации и контейнерами состояний.
  • Node.js Application помогает настроить проект для работы в среде выполнения Node.js. Шаблон позволяет включить экспериментальный пакет kotlinx-nodejs, о котором мы рассказывали в этом посте.

Игнорирование ошибок компиляции (экспериментальная функциональность)

В этой версии мы рады представить новую функцию в IR-компиляторе Kotlin/JSигнорирование ошибок компиляции. Это позволяет оценить работу вашего приложения, даже если оно находится в состоянии, в котором обычно не компилируется. Например, когда вы выполняете сложный рефакторинг или работаете с частью системы, которая совершенно не связана с ошибкой компиляции. В этом режиме компилятор игнорирует любой ошибочный код и заменяет его runtime-исключениями, а не отказывает в компиляции.

В Kotlin 1.4.20 предусмотрено два режима игнорирования ошибок компиляции:

  • В режиме SEMANTIC компилятор принимает синтаксически корректный код, который не имеет смысла семантически. Например, это может быть оператор с несоответствием типов (val x: String = 3).
  • В режиме SYNTAX компилятор принимает любой код, даже если в нем содержатся синтаксические ошибки. Независимо от того, что вы напишете, компилятор все равно будет пытаться создать работающий исполняемый файл.

Поскольку это экспериментальная функция, чтобы включить игнорирование ошибок компиляции, нужно указать параметр компилятора. Он доступен только в IR-компиляторе Kotlin/JS: добавьте в файл build.gradle.kts следующий код:

Надеемся, компиляция с ошибками поможет вам сократить цикл обратной связи и ускорить итерации при работе над проектами Kotlin/JS. Сообщайте нам о любых обнаруженных ошибках через баг-трекер — мы очень ценим вашу помощь.

Мы продолжим улучшать эту функцию и планируем реализовать более глубокую интеграцию с Kotlin/JS Gradle DSL.

Kotlin/Native

В версии 1.4.20 производительность остается одним из главных приоритетов Kotlin/Native. Главное нововведение — прототип нового механизма escape-анализа, который мы планируем дорабатывать и улучшать в следующих версиях. Кроме того, есть много небольших оптимизаций, например ускоренная проверка на принадлежность к диапазону (in).

Мы также уделили много внимания исправлению ошибок: устранили ряд старых проблем, а также проблемы, обнаруженные в функциональности, появившейся в версии 1.4, в частности в механизме использования общего кода. Исправления также коснулись несоответствий в поведении Kotlin/Native и Kotlin/JVM в особых случаях, таких как инициализация свойств и взаимодействие equals и hashCode с функциональными ссылками.

Наконец, мы расширили совместимость с Objective-C: теперь можно оборачивать исключения Objective-C в исключения Kotlin, что позволяет обрабатывать их в Kotlin-коде.

Escape-анализ

Escape-анализ — это процесс, который используется компилятором, чтобы определить, может ли объект быть помещен в стек или же ему следует «сбежать» в кучу. Размещение в стеке повышает производительность и не требует сборки мусора впоследствии.

Хотя в Kotlin/Native уже был локальный escape-анализ, сейчас мы представляем прототип нового, более эффективного механизма глобального анализа. Анализ выполняется в отдельную фазу компиляции для релизных сборок (с параметром компилятора -opt).

Прототип уже показал довольно многообещающие результаты: по нашим измерениям производительность в среднем увеличилась на 10%. Мы ищем способы оптимизировать алгоритм, чтобы он находил больше объектов, которые можно разместить в стеке, и еще больше ускорял программу.

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

Если вы захотите отключить фазу escape-анализа, используйте параметр компилятора -Xdisable-phases=EscapeAnalysis.

Возможность оборачивать исключения Objective-C

В Kotlin и Objective-C исключения сильно отличаются по своему назначению. В Objective-C их использование обычно ограничивается поиском ошибок во время разработки. Но технически библиотеки Objective-C могут вызывать исключения и во время выполнения. Раньше в Kotlin/Native не было возможности обрабатывать такие исключения, а обнаружение NSException, пришедшего из библиотеки, вызывало завершение всей программы Kotlin/Native.

В версии 1.4.20 мы добавили возможность обрабатывать такие исключения во время выполнения и избегать завершения программы. Теперь можно обернуть NSException в ForeignException для дальнейшей обработки в Kotlin-коде. В таком случае ForeignExeption будет содержать ссылку на исходное исключение NSException, что позволит получить информацию о причине сбоя.

Чтобы включить обертывание Objective-C исключений, укажите параметр -Xforeign-exception-mode objc-wrap в вызове cinterop или добавьте свойство foreignExceptionMode = objc-wrap в файл .def. Если вы пользуетесь интеграцией с CocoaPods, укажите данный параметр в блоке pod {} билд-скрипта зависимости:

Поведение по умолчанию осталось неизменным: при возникновении исключения в коде Objective-C программа прерывается.

Улучшения в плагине CocoaPods

Улучшенное выполнение задач

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

Расширенный DSL

В версии 1.4.20 мы расширили возможности DSL для добавления зависимостей CocoaPods в Kotlin-проект.

Помимо локальных подов и подов из репозитория CocoaPods, теперь можно добавлять зависимости от следующих типов библиотек:

  • Библиотека из пользовательского репозитория спецификаций.
  • Удаленная библиотека из Git-репозитория.
  • Библиотека из архива (также доступна по произвольному HTTP-адресу).
  • Статическая библиотека.
  • Библиотека с настраиваемыми cinterop-параметрами.

Предыдущий DSL-синтаксис по-прежнему поддерживается.

Рассмотрим пару изменений DSL на следующих примерах:

  • Зависимость от удаленной библиотеки из Git-репозитория. Вы можете указать определенный тег, коммит или ветку с помощью соответствующих ключевых слов, например:

    Вы можете комбинировать эти ключевые слова, чтобы получить нужную версию пода.

  • Зависимость от библиотеки из пользовательского репозитория. Используйте специальный параметр specRepos:

Больше примеров можно найти на GitHub.

Обновленная интеграция с Xcode

Для правильной работы с Xcode необходимы некоторые изменения в Podfile:

  • Если Kotlin-под имеет зависимости от Git, HTTP или specRepo-пода, их также нужно указать в Podfile. Например, если вы добавляете зависимость от AFNetworking из репозитория CocoaPods, объявите ее в Podfile:

    pod 'AFNetworking'
  • При добавлении библиотеки из пользовательской спецификации вы должны указать расположение спецификаций в начале Podfile:

    source 'https://github.com/Kotlin/kotlin-cocoapods-spec.git'
    
    target 'kotlin-cocoapods-xcproj' do
      // ... other Pods ...
      pod 'example'
    end

В IntelliJ IDEA ошибки интеграции теперь подробно описываются, поэтому, если у вас возникнут проблемы с Podfile, вы сразу узнаете, как их исправить.

Взгляните на ветку withXcproject в примере Kotlin-кода с CocoaPods. Там есть пример интеграции с существующим Xcode-проектом под названием kotlin-cocoapods-xcproj.

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

Мы добавили поддержку новых библиотек, поставляемых с Xcode 12. Можете смело использовать их в своем Kotlin-коде.

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

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

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

Обратите внимание: не стоит добавлять пустой артефакт без классификатора в корневой модуль вашей библиотеки для соответствия требованиям репозиториев, таких как Maven Central, — это приведет к конфликту с артефактами метаданных, которые теперь включены в этот модуль.

Совместимость с библиотеками, опубликованными в версии 1.4.20

Если вы включили поддержку иерархических проектов и хотите использовать мультиплатформенную библиотеку, которая была опубликована с такой поддержкой в версии Kotlin 1.4.20+, вам придется обновить Kotlin в своем проекте как минимум до версии 1.4.20.

Если вы являетесь автором мультиплатформенной библиотеки и публикуете ее в версии Kotlin 1.4.20+ с поддержкой иерархических проектов, имейте в виду, что пользователи более ранних версий языка, у которых также включена поддержка иерархической структуры проекта, не смогут пользоваться вашей библиотекой. Им нужно будет обновить Kotlin как минимум до версии 1.4.20.

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

Подробнее о публикации мультиплатформенной библиотеки.

Изменения в стандартной библиотеке

Расширения для java.nio.file.Path

Начиная с версии 1.4.20, в стандартной библиотеке появились экспериментальные расширения для java.nio.file.Path.

Идиоматическое использование современного API файлов JVM в Kotlin теперь похоже на работу с расширениями java.io.File из пакета kotlin.io. Больше не нужно вызывать статические методы Files, потому что большинство из них теперь доступны как расширения для типа Path.

Расширения находятся в пакете kotlin.io.path. Поскольку сам Path доступен в JDK 7 и более поздних версиях, расширения помещены в модуль kotlin-stdlib-jdk7. Чтобы их использовать, необходимо включить экспериментальную аннотацию ExperimentalPathApi.

Хотим отдельно поблагодарить AJ Alt за отправку первоначального пул-реквеста с этими расширениями.

Ускоренное выполнение функции String.replace

Мы всегда рады, когда пользователи Kotlin предлагают улучшения, и это одно из таких. В этой версии мы изменили реализацию функции String.replace().

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

В некоторых случаях это ускоряет выполнение функции.

Прекращение поддержки Kotlin Android Extensions

За время своего существования плагин Kotlin Android Extensions сыграл немалую роль в росте популярности Kotlin в экосистеме Android. Он предоставлял разработчикам удобные и эффективные инструменты для сокращения стереотипного кода:

  • Synthetic views (kotlinx.android.synthetics) для взаимодействия с пользовательским интерфейсом.
  • Генератор реализации Parcelable (@Parcelize) для передачи объектов как Parcel.

Изначально мы планировали добавить дополнительные компоненты в kotlin-android-extensions. Но этого не произошло, и пользователи просили нас разделить плагин на независимые части.

Экосистема Android постоянно развивается, и у разработчиков появляются новые инструменты, упрощающие работу. Некоторые задачи, за которые раньше отвечал плагин Kotlin Android Extensions, теперь решаются собственными механизмами Google. Например, если говорить о компактном синтаксисе для взаимодействия с пользовательским интерфейсом, теперь есть Android Jetpack, поддерживающий view binding, который заменяет findViewById так же, как synthetic views.

Учитывая эти два фактора, мы решили отказаться от синтетиков в пользу view binding и вынести генератор реализации Parcelable в отдельный плагин.

В версии 1.4.20 мы убрали генератор реализации Parcelable из kotlin-android-extensions и запустили цикл прекращения поддержки остальной части плагина. Пока что synthetic views продолжат работать с предупреждением о прекращении поддержки. В дальнейшем вам нужно будет перейти на другое решение. Вот рекомендации по переводу Android-проектов с synthetic views на view bindings.

Генератор реализации Parcelable теперь доступен через новый плагин kotlin-parcelize. Используйте этот плагин вместо kotlin-android-extensions или в дополнение к нему, если планируете и дальше работать с синтетиками. Аннотация @Parcelize переместилась в пакет kotlinx.parcelize.

Как обновиться

Прежде чем обновлять проекты до последней версии языка, попробуйте новые возможности Kotlin и стандартной библиотеки на сайте play.kotl.in.

В IntelliJ IDEA и Android Studio обновите Kotlin-плагин до версии 1.4.20. Узнайте, как это делается.

Если вы работаете над проектами, созданными с помощью предыдущих версий Kotlin, укажите версию Kotlin 1.4.20 в конфигурации проекта. Подробности — в разделах документации о Gradle и Maven.

Скачать компилятор для командной строки можно с GitHub-страницы релиза.

С данным релизом вы можете использовать следующие версии библиотек:

Версии библиотек kotlin-wrappers (kotlin-react и др.) вы найдете в соответствующем репозитории.

Подробные сведения о релизе и список совместимых библиотек также доступны здесь.

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

Внешние контрибьюторы

Мы очень благодарны всем внешним контрибьюторам, чьи пул-реквесты были включены в этот релиз:

Jinseong Jeon
Toshiaki Kameyama
Steven Schäfer
Mads Ager
Mark Punzalan
Ivan Gavrilovic
pyos
Jim Sproch
Kristoffer Andersen
Александрина Стрельцова
cketti
Konstantin Virolainen
AJ Alt Henrik Tunedal
Juan Chen
KotlinIsland,
Валерий Вырва
Alex Chmyr
Алексей Кудрявцев
Андрей Матвеев
Aurimas Liutikas
Dat Trieu
Dereck Bridie
Efeturi Money
Elijah Verdoorn
Enteerman
fee1-dead
Francesco Vasco
Gia Thuan Lam
Guillaume Darmont
Jake Wharton
Julian Kotrba
Kevin Bierhoff
Matthew Gharrity
Matts966
Raluca Sauciuc
Ryan Nett
Sebastian Kaspari
Владимир Кривошеев
n-p-s
Pavlos-Petros Tournaris
Robert Bares
Yoshinori Isogai
Kris
Derek Bodin
Dominik Wuttke
Sam Wang
Uzi Landsmann
Yuya Urano
Norbert Nogacki
Alexandre Juca

Ваша команда Kotlin
The Drive to Develop

Discover more