Что нового в Kotlin 1.4.0
Выпущено: 17 августа 2020 года
В Kotlin 1.4.0 мы внедрили ряд улучшений во всех его компонентах, с фокусом на качество и производительность. Ниже приведен список наиболее важных изменений в Kotlin 1.4.0.
Языковые возможности и улучшения
Kotlin 1.4.0 поставляется с различными языковыми возможностями и улучшениями. К ним относятся:
Преобразования SAM для Kotlin-интерфейсов
До Kotlin 1.4.0 преобразования SAM (Single Abstract Method) можно было применять только при работе с Java-методами и Java-интерфейсами из Kotlin. Теперь вы можете использовать преобразования SAM для Kotlin-интерфейсов. Для этого пометьте Kotlin-интерфейс явно как функциональный с помощью fun модификатора.
Преобразование SAM применяется, если в качестве аргумента передается лямбда-выражение, когда в качестве параметра ожидается интерфейс с единственным абстрактным методом. В этом случае компилятор автоматически преобразует лямбда-выражение в экземпляр класса, реализующего абстрактный член функции.
fun interface IntPredicate {
fun accept(i: Int): Boolean
}
val isEven = IntPredicate { it % 2 == 0 }
fun main() {
println("Is 7 even? - ${isEven.accept(7)}")
}
Дополнительную информацию о функциональных интерфейсах и преобразованиях SAM в Kotlin см. здесь.
Режим явного API для авторов библиотек
Компилятор Kotlin предлагает режим явного API для авторов библиотек. В этом режиме компилятор выполняет дополнительные проверки, которые помогают сделать API библиотеки более понятным и согласованным. Он добавляет следующие требования к объявлениям, доступным для публичного API библиотеки:
Требуются модификаторы видимости для объявлений, если по умолчанию они доступны в публичном API. Это помогает гарантировать, что ни одно объявление не будет случайно доступно для публичного API.
Требуется явное указание типов для свойств и функций, доступных в публичном API. Это гарантирует, что пользователи API знают типы членов API, которые они используют.
В зависимости от вашей конфигурации, эти явные API могут генерировать ошибки (строгий режим) или предупреждения (режим предупреждения). Некоторые типы объявлений исключаются из таких проверок ради удобочитаемости и здравого смысла:
первичные конструкторы
свойства классов данных
геттеры и сеттеры свойств
overrideметоды
Режим явного API анализирует только исходные файлы модуля.
Для компиляции модуля в режиме явного API добавьте следующие строки в ваш скрипт сборки Gradle:
kotlin {
// for strict mode
explicitApi()
// or
explicitApi = ExplicitApiMode.Strict
// for warning mode
explicitApiWarning()
// or
explicitApi = ExplicitApiMode.Warning
}
kotlin {
// for strict mode
explicitApi()
// or
explicitApi = 'strict'
// for warning mode
explicitApiWarning()
// or
explicitApi = 'warning'
}
При использовании компилятора командной строки переключитесь на режим явного API, добавив -Xexplicit-api параметр компилятора со значением strict или warning.
-Xexplicit-api={strict|warning}
Смешивание именованных и позиционных аргументов
В Kotlin 1.3, когда вы вызывали функцию с именованными аргументами, вам нужно было поместить все аргументы без имен (позиционные аргументы) перед первым именованным аргументом. Например, вы могли вызвать f(1, y = 2), но не могли вызвать f(x = 1, 2).
Это было действительно раздражающим, когда все аргументы были на своих местах, но вы хотели указать имя для одного аргумента посередине. Это особенно полезно для того, чтобы абсолютно четко указать, к какому атрибуту относится значение boolean или null.
В Kotlin 1.4 такого ограничения нет — теперь вы можете указать имя аргумента посередине набора позиционных аргументов. Кроме того, вы можете смешивать позиционные и именованные аргументы любым способом, при условии, что они сохраняют правильный порядок.
fun reformat(
str: String,
uppercaseFirstLetter: Boolean = true,
wordSeparator: Char = ' '
) {
// ...
}
//Function call with a named argument in the middle
reformat("This is a String!", uppercaseFirstLetter = false , '-')
Завершающая запятая
В Kotlin 1.4 теперь можно добавлять завершающую запятую в списки аргументов и параметров, when записи и компоненты деструктурирующих объявлений. С завершающей запятой вы можете добавлять новые элементы и изменять их порядок без добавления или удаления запятых.
Это особенно полезно, если вы используете многострочный синтаксис для параметров или значений. После добавления завершающей запятой вы можете легко переставлять строки с параметрами или значениями.
fun reformat(
str: String,
uppercaseFirstLetter: Boolean = true,
wordSeparator: Character = ' ', //trailing comma
) {
// ...
}
val colors = listOf(
"red",
"green",
"blue", //trailing comma
)
Улучшения вызываемых ссылок
Kotlin 1.4 поддерживает больше случаев использования вызываемых ссылок:
Ссылки на функции с значениями по умолчанию
Ссылки на функции в
Unit-возвращающих функцияхСсылки, которые адаптируются в зависимости от количества аргументов в функции
Преобразование suspend для вызываемых ссылок
Ссылки на функции с значениями по умолчанию
Теперь вы можете использовать вызываемые ссылки на функции с значениями по умолчанию. Если вызываемая ссылка на функцию foo не принимает аргументов, используется значение по умолчанию 0.
fun foo(i: Int = 0): String = "$i!"
fun apply(func: () -> String): String = func()
fun main() {
println(apply(::foo))
}
Ранее вам приходилось писать дополнительные перегрузки для функции apply для использования значений по умолчанию.
// some new overload fun applyInt(func: (Int) -> String): String = func(0)
Ссылки на функции в функциях, возвращающих Unit
В Kotlin 1.4 вы можете использовать вызываемые ссылки на функции, возвращающие любой тип, в Unit-возвращающих функциях. До Kotlin 1.4 вы могли использовать только лямбда-аргументы в этом случае. Теперь вы можете использовать как лямбда-аргументы, так и вызываемые ссылки.
fun foo(f: () -> Unit) { }
fun returnsInt(): Int = 42
fun main() {
foo { returnsInt() } // this was the only way to do it before 1.4
foo(::returnsInt) // starting from 1.4, this also works
}
Ссылки, которые адаптируются в зависимости от количества аргументов в функции
Теперь вы можете адаптировать вызываемые ссылки на функции при передаче переменного количества аргументов (vararg). Вы можете передавать любое количество параметров одного типа в конце списка переданных аргументов.
fun foo(x: Int, vararg y: String) {}
fun use0(f: (Int) -> Unit) {}
fun use1(f: (Int, String) -> Unit) {}
fun use2(f: (Int, String, String) -> Unit) {}
fun test() {
use0(::foo)
use1(::foo)
use2(::foo)
}
Преобразование suspend для вызываемых ссылок
В дополнение к преобразованию suspend для лямбда-выражений, Kotlin теперь поддерживает преобразование suspend для вызываемых ссылок, начиная с версии 1.4.0.
fun call() {}
fun takeSuspend(f: suspend () -> Unit) {}
fun test() {
takeSuspend { call() } // OK before 1.4
takeSuspend(::call) // In Kotlin 1.4, it also works
}
Использование break и continue внутри выражений when, включенных в циклы
В Kotlin 1.3 вы не могли использовать неквалифицированные break и continue внутри when выражений, включенных в циклы. Причина заключалась в том, что эти ключевые слова были зарезервированы для возможного продолжения выполнения в when выражениях.
Поэтому, если вы хотели использовать break и continue внутри when выражений в циклах, вам приходилось помечать их, что становилось довольно громоздким.
fun test(xs: List<Int>) {
LOOP@for (x in xs) {
when (x) {
2 -> continue@LOOP
17 -> break@LOOP
else -> println(x)
}
}
}
В Kotlin 1.4 вы можете использовать break и continue без меток внутри when выражений, включенных в циклы. Они ведут себя так, как ожидается, завершая ближайший внешний цикл или переходя к его следующей итерации.
fun test(xs: List<Int>) {
for (x in xs) {
when (x) {
2 -> continue
17 -> break
else -> println(x)
}
}
}
Поведение продолжения выполнения внутри when находится на стадии дальнейшей разработки.
Новые инструменты в IDE
С Kotlin 1.4 вы можете использовать новые инструменты в IntelliJ IDEA, чтобы упростить разработку на Kotlin:
Новый гибкий мастер проектов
С новым гибким мастером проектов Kotlin вы можете легко создавать и настраивать различные типы проектов Kotlin, включая проекты для нескольких платформ, настройку которых без графического интерфейса может быть сложно.

Новый мастер проектов Kotlin прост и гибкий:
Выберите шаблон проекта в зависимости от того, что вы хотите сделать. В будущем будет добавлено больше шаблонов.
Выберите систему сборки – Gradle (Kotlin или Groovy DSL), Maven или IntelliJ IDEA.
Мастер проектов Kotlin будет отображать только поддерживаемые системы сборки для выбранного шаблона проекта.Предварительный просмотр структуры проекта непосредственно на главном экране.
Затем вы можете завершить создание проекта или, по желанию, настроить проект на следующем экране:
Добавить/удалить модули и целевые платформы, поддерживаемые для этого шаблона проекта.
Настройка параметров модуля и целевой платформы, например, версия целевой JVM, шаблон цели и фреймворк для тестирования.

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

Теперь вы можете:
Легко проверять состояние каждой корутины.
Просматривать значения локальных и захваченных переменных для как выполняемых, так и приостановленных корутин.
Просматривать полный стек создания корутины, а также стек вызовов внутри корутины. Стек включает все фреймы с значениями переменных, даже те, которые терялись во время стандартной отладки.
Если вам нужен полный отчет, содержащий состояние каждой корутины и ее стек, щелкните правой кнопкой мыши внутри вкладки Корутины, а затем нажмите Получить дамп корутин. В настоящее время дамп корутин довольно прост, но мы сделаем его более читаемым и полезным в будущих версиях Kotlin.
Узнайте больше об отладке корутин в этой статье блога и документации IntelliJ IDEA.
Новый компилятор
Новый компилятор Kotlin будет очень быстрым; он объединит все поддерживаемые платформы и предоставит API для расширений компилятора. Это проект на долгосрочную перспективу, и мы уже выполнили несколько шагов в Kotlin 1.4.0:
Новый, более мощный алгоритм вывода типов включён по умолчанию.
Новые бэкэнды JVM и JS IR. Они станут по умолчанию после стабилизации.
Новый более мощный алгоритм вывода типов
Kotlin 1.4 использует новый, более мощный алгоритм вывода типов. Этот новый алгоритм уже был доступен для тестирования в Kotlin 1.3 путём указания параметра компилятора, и теперь он используется по умолчанию. Полный список исправленных проблем в новом алгоритме можно найти в YouTrack. Здесь вы можете найти некоторые из самых заметных улучшений:
Умные преобразования типов для последнего выражения лямбда-выражения
Преобразование SAM для интерфейсов Java с различными аргументами
Больше случаев, когда тип выводится автоматически
Новый алгоритм вывода типов выводит типы во многих случаях, где старый алгоритм требовал явного указания. Например, в следующем примере тип параметра лямбда-выражения it правильно выводится как String?:
//sampleStart
val rulesMap: Map<String, (String?) -> Boolean> = mapOf(
"weak" to { it != null },
"medium" to { !it.isNullOrBlank() },
"strong" to { it != null && "^[a-zA-Z0-9]+$".toRegex().matches(it) }
)
//sampleEnd
fun main() {
println(rulesMap.getValue("weak")("abc!"))
println(rulesMap.getValue("strong")("abc"))
println(rulesMap.getValue("strong")("abc!"))
}
В Kotlin 1.3 вам нужно было вводить явный параметр лямбда-выражения или заменять to на конструктор Pair с явными параметрами дженериков, чтобы это работало.
Умные преобразования типов для последнего выражения лямбда-выражения
В Kotlin 1.3 последнее выражение внутри лямбда-выражения не подвергалось умному преобразованию типов, если вы не указывали ожидаемый тип. Таким образом, в следующем примере Kotlin 1.3 выводит String? как тип переменной result:
val result = run {
var str = currentValue()
if (str == null) {
str = "test"
}
str // the Kotlin compiler knows that str is not null here
}
// The type of 'result' is String? in Kotlin 1.3 and String in Kotlin 1.4
В Kotlin 1.4 благодаря новому алгоритму вывода типов последнее выражение внутри лямбда-выражения подвергается умному преобразованию типов, и этот новый, более точный тип используется для вывода типа результирующего лямбда-выражения. Таким образом, тип переменной result становится String.
В Kotlin 1.3 часто приходилось добавлять явные преобразования типов (либо !! или преобразования типов, как as String), чтобы такие случаи работали, и теперь эти преобразования стали не нужны.
Умные преобразования типов для вызываемых ссылок
В Kotlin 1.3 вы не могли получить доступ к ссылке на член типа, подвергшегося умному преобразованию типов. Теперь в Kotlin 1.4 вы можете:
import kotlin.reflect.KFunction
sealed class Animal
class Cat : Animal() {
fun meow() {
println("meow")
}
}
class Dog : Animal() {
fun woof() {
println("woof")
}
}
//sampleStart
fun perform(animal: Animal) {
val kFunction: KFunction<*> = when (animal) {
is Cat -> animal::meow
is Dog -> animal::woof
}
kFunction.call()
}
//sampleEnd
fun main() {
perform(Cat())
}
Вы можете использовать разные ссылки на члены animal::meow и animal::woof после того, как переменная animal была преобразована в конкретные типы Cat и Dog. После проверок типов вы можете получить доступ к ссылкам на члены, соответствующим подтипам.
Улучшенный вывод типов для делегированных свойств
Тип делегированного свойства не учитывался при анализе выражения делегата, следующего за ключевым словом by. Например, следующий код не компилировался ранее, но теперь компилятор правильно выводит типы параметров old и new как String?:
import kotlin.properties.Delegates
fun main() {
var prop: String? by Delegates.observable(null) { p, old, new ->
println("$old → $new")
}
prop = "abc"
prop = "xyz"
}
Преобразование SAM для интерфейсов Java с различными аргументами
Kotlin поддерживает преобразования SAM для интерфейсов Java с самого начала, но существовал один случай, который не поддерживался, что иногда доставляло неудобства при работе с существующими библиотеками Java. Если вы вызывали метод Java, принимающий два интерфейса SAM в качестве параметров, оба аргумента должны были быть либо лямбдами, либо обычными объектами. Вы не могли передать один аргумент в качестве лямбды, а другой — в качестве объекта.
Новый алгоритм исправляет эту проблему, и вы можете передавать лямбду вместо интерфейса SAM в любом случае, что является естественным ожиданием.
// FILE: A.java
public class A {
public static void foo(Runnable r1, Runnable r2) {}
}
// FILE: test.kt
fun test(r1: Runnable) {
A.foo(r1) {} // Works in Kotlin 1.4
}
Интерфейсы Java SAM в Kotlin
В Kotlin 1.4 вы можете использовать интерфейсы Java SAM в Kotlin и применять к ним преобразования SAM.
import java.lang.Runnable
fun foo(r: Runnable) {}
fun test() {
foo { } // OK
}
В Kotlin 1.3 вам пришлось бы объявить функцию foo выше в коде Java, чтобы выполнить преобразование SAM.
Объединённые бэкэнды и расширяемость
В Kotlin у нас есть три бэкэнда, которые генерируют исполняемые файлы: Kotlin/JVM, Kotlin/JS и Kotlin/Native. Kotlin/JVM и Kotlin/JS не имеют много общего кода, поскольку они разрабатывались независимо друг от друга. Kotlin/Native основан на новой инфраструктуре, построенной вокруг промежуточного представления (IR) для кода Kotlin.
Сейчас мы мигрируем Kotlin/JVM и Kotlin/JS в одно и то же IR. В результате все три бэкэнда разделяют много логики и имеют унифицированную конвейерную линию. Это позволяет нам реализовывать большинство функций, оптимизаций и исправлений ошибок только один раз для всех платформ. Оба новых бэкэнда на основе IR находятся в стадии Альфа.
Общеинфраструктурный бэкэнд также открывает возможность для расширений компилятора для нескольких платформ. Вы сможете подключиться к конвейеру и добавить пользовательскую обработку и преобразования, которые автоматически будут работать на всех платформах.
Мы рекомендуем использовать новые бэкэнды JVM IR и JS IR, которые в настоящее время находятся в стадии Альфа, и поделиться своими отзывами с нами.
Kotlin/JVM
Kotlin 1.4.0 включает ряд улучшений, специфичных для JVM, таких как:
Новый бэкенд JVM IR
Вместе с Kotlin/JS мы мигрируем Kotlin/JVM на унифицированный бэкенд IR, что позволяет нам реализовывать большинство функций и исправлять ошибки один раз для всех платформ. Вы также сможете извлечь выгоду из этого, создавая кроссплатформенные расширения, которые будут работать на всех платформах.
Kotlin 1.4.0 пока не предоставляет публичный API для таких расширений, но мы тесно сотрудничаем с нашими партнёрами, включая Jetpack Compose, которые уже создают свои плагины компилятора, используя наш новый бэкенд.
Мы рекомендуем вам опробовать новый бэкенд Kotlin/JVM, который сейчас находится в стадии альфа-версии, и сообщить о любых проблемах и пожеланиях по функциям в наш трекер задач. Это поможет нам унифицировать конвейеры компилятора и быстрее интегрировать расширения компилятора, такие как Jetpack Compose, в сообщество Kotlin.
Для активации нового бэкенда JVM IR укажите дополнительный параметр компилятора в вашем файле Gradle:
kotlinOptions.useIR = true
При использовании компилятора командной строки добавьте параметр компилятора -Xuse-ir.
Новые режимы генерации методов по умолчанию
При компиляции кода Kotlin для целей JVM 1.8 и выше, вы могли компилировать неабстрактные методы Kotlin-интерфейсов в методы Java default. Для этой цели существовал механизм, включающий аннотацию @JvmDefault, чтобы отмечать такие методы, и параметр компилятора -Xjvm-default, который активировал обработку этой аннотации.
В версии 1.4.0 мы добавили новый режим генерации методов по умолчанию: -Xjvm-default=all компилирует все неабстрактные методы Kotlin-интерфейсов в методы Java default. Для совместимости с кодом, использующим интерфейсы, скомпилированные без default, мы также добавили режим all-compatibility.
Дополнительную информацию о методах по умолчанию в Java-взаимодействии см. в документации по взаимодействию и этой статье блога.
Единый тип исключения для проверок на null
Начиная с Kotlin 1.4.0, все проверки на null во время выполнения будут выбрасывать исключение java.lang.NullPointerException вместо KotlinNullPointerException, IllegalStateException, IllegalArgumentException, и TypeCastException. Это относится к: оператору !!, проверкам параметров на null в преамбуле метода, проверкам на null платформенно-типизированных выражений и оператору as с типом не null. Это не относится к lateinit проверкам на null и явным вызовам функций библиотек, таким как checkNotNull или requireNotNull.
Это изменение увеличивает количество возможных оптимизаций проверок на null, которые могут быть выполнены либо Kotlin-компилятором, либо различными инструментами обработки байткода, такими как оптимизатор R8 Android Android.
Обратите внимание, что с точки зрения разработчика, изменения будут не очень значительными: код Kotlin будет выбрасывать исключения с теми же сообщениями об ошибках, что и раньше. Тип исключения меняется, но передаваемая информация остаётся той же.
Аннотации типов в байткоде JVM
Kotlin теперь может генерировать аннотации типов в байткоде JVM (версия целевого языка 1.8+), так что они станут доступны в Java-рефлексии во время выполнения. Для генерации аннотации типа в байткоде выполните следующие действия:
Убедитесь, что ваша объявленная аннотация имеет правильную цель применения аннотации (Java's
ElementType.TYPE_USEили Kotlin'sAnnotationTarget.TYPE) и сохранность (AnnotationRetention.RUNTIME).Скомпилируйте объявление класса аннотации в байткод JVM с целевой версией 1.8+. Вы можете указать её с помощью параметра компилятора
-jvm-target=1.8.Скомпилируйте код, использующий аннотацию, в байткод JVM с целевой версией 1.8+ (
-jvm-target=1.8) и добавьте параметр компилятора-Xemit-jvm-type-annotations.
Обратите внимание, что аннотации типа из стандартной библиотеки пока не генерируются в байткоде, так как стандартная библиотека скомпилирована с целевой версией 1.6.
Пока поддерживаются только базовые случаи:
Аннотации типа на параметрах методов, типах возвращаемых значений методов и типах свойств;
Инвариантные проекции аргументов типа, такие как
Smth<@Ann Foo>,Array<@Ann Foo>.
В следующем примере аннотация @Foo на типе String может быть сгенерирована в байткоде и затем использована кодом библиотеки:
@Target(AnnotationTarget.TYPE)
annotation class Foo
class A {
fun foo(): @Foo String = "OK"
}
Kotlin/JS
На платформе JS, Kotlin 1.4.0 предоставляет следующие улучшения:
Новая Gradle DSL
Плагин Gradle kotlin.js поставляется с изменённой Gradle DSL, которая предоставляет ряд новых параметров конфигурации и более точно соответствует DSL, используемому плагином kotlin-multiplatform. Некоторые из самых важных изменений включают:
Явные переключатели для создания исполняемых файлов с помощью
binaries.executable(). Подробнее об исполнении Kotlin/JS и его среде здесь.Конфигурация CSS- и стилистических загрузчиков webpack внутри конфигурации Gradle с помощью
cssSupport. Подробнее об использовании CSS- и стилистических загрузчиков здесь.Улучшенное управление зависимостями npm, с обязательными версиями или диапазонами версий semver, а также поддержка разработки, зависимостей и дополнительных зависимостей npm с помощью
devNpm,optionalNpmиpeerNpm. Подробнее об управлении зависимостями npm пакетов прямо из Gradle здесь.Более сильные интеграции с Dukat, генератором внешних объявлений Kotlin. Внешние объявления теперь могут генерироваться во время сборки или генерироваться вручную с помощью задачи Gradle.
Новый бэкенд JS IR
Бэкенд IR для Kotlin/JS, который в настоящее время имеет Альфа стабильность, предоставляет некоторые новые функции, специфичные для целевого языка Kotlin/JS, которые сосредоточены на размере генерируемого кода через удаление мертвого кода, улучшенное взаимодействие с JavaScript и TypeScript и другие.
Для активации бэкенда Kotlin/JS IR установите ключ kotlin.js.compiler=ir в вашей gradle.properties, или передайте тип компилятора IR в функцию js вашего файла Gradle:
kotlin {
js(IR) { // or: LEGACY, BOTH
// ...
}
binaries.executable()
}
Более подробную информацию о настройке нового бэкенда см. в документации компилятора Kotlin/JS IR.
Благодаря новой аннотации @JsExport и возможности генерации определений TypeScript из кода Kotlin, бэкенд компилятора Kotlin/JS IR улучшает взаимодействие JavaScript и TypeScript. Это также упрощает интеграцию кода Kotlin/JS с существующими инструментами, для создания гибридных приложений и использования функциональности совместного использования кода в кроссплатформенных проектах.
Узнайте больше о доступных функциях бэкенда компилятора Kotlin/JS IR.
Kotlin/Native
В версии 1.4.0 Kotlin/Native получил значительное количество новых функций и улучшений, включая:
Поддержка приостанавливаемых функций Kotlin в Swift и Objective-C
Генерация .dSYMs релизной сборки по умолчанию для Apple целей
Поддержка приостанавливаемых функций Kotlin в Swift и Objective-C
В версии 1.4.0 мы добавили базовую поддержку приостанавливаемых функций в Swift и Objective-C. Теперь, когда вы компилируете модуль Kotlin в Apple фреймворк, приостанавливаемые функции доступны в нём как функции с обратными вызовами (completionHandler в терминологии Swift/Objective-C). Когда у вас есть такие функции в заголовке сгенерированного фреймворка, вы можете вызывать их из своего Swift или Objective-C кода, а также переопределять их.
Например, если вы напишете эту функцию Kotlin:
suspend fun queryData(id: Int): String = ...
...то вы можете вызвать её из Swift следующим образом:
queryData(id: 17) { result, error in
if let e = error {
print("ERROR: \(e)")
} else {
print(result!)
}
}
Узнайте больше о работе с приостанавливаемыми функциями в Swift и Objective-C.
Поддержка Objective-C дженериков по умолчанию
Предыдущие версии Kotlin предоставляли экспериментальную поддержку дженериков в Objective-C взаимодействии. С версии 1.4.0 Kotlin/Native генерирует Apple фреймворки с дженериками из Kotlin кода по умолчанию. В некоторых случаях это может нарушить существующий Objective-C или Swift код, вызывающий Kotlin фреймворки. Чтобы заголовок фреймворка был написан без дженериков, добавьте опцию компилятора -Xno-objc-generics.
kotlin {
targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget> {
binaries.all {
freeCompilerArgs += "-Xno-objc-generics"
}
}
}
Пожалуйста, обратите внимание, что все подробности и ограничения, указанные в документации по взаимодействию с Objective-C, по-прежнему актуальны.
Обработка исключений в Objective-C/Swift взаимодействии
В версии 1.4.0 мы немного изменили Swift API, сгенерированный из Kotlin, в отношении способа преобразования исключений. Существует фундаментальное различие в обработке ошибок между Kotlin и Swift. Все исключения Kotlin не проверяемые, в то время как Swift имеет только проверяемые ошибки. Таким образом, чтобы код Swift знал об ожидаемых исключениях, функции Kotlin должны быть помечены аннотацией @Throws, указывающей список потенциальных классов исключений.
При компиляции в Swift или Objective-C фреймворк функции, которые имеют или наследуют аннотацию @Throws, представляются как методы, генерирующие NSError* в Objective-C и как throws методы в Swift.
Ранее любые исключения, кроме RuntimeException и Error, передавались как NSError. Теперь это поведение изменяется: теперь NSError выбрасывается только для исключений, которые являются экземплярами классов, указанных в качестве параметров аннотации @Throws (или их подклассов). Другие Kotlin исключения, которые достигают Swift/Objective-C, считаются необработанными и приводят к завершению программы.
Генерация release .dSYMs по умолчанию для Apple целей
Начиная с версии 1.4.0, компилятор Kotlin/Native по умолчанию производит файлы символов отладки (.dSYMs) для релизных бинарников на платформах Darwin. Это можно отключить с помощью опции компилятора -Xadd-light-debug=disable. На других платформах эта опция отключена по умолчанию. Чтобы переключить эту опцию в Gradle, используйте:
kotlin {
targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget> {
binaries.all {
freeCompilerArgs += "-Xadd-light-debug={enable|disable}"
}
}
}
Улучшения производительности
Kotlin/Native получил ряд улучшений производительности, ускоряющих как процесс разработки, так и выполнение. Вот некоторые примеры:
Для повышения скорости выделения объектов мы теперь предлагаем выделение памяти mimalloc в качестве альтернативы системному выделению. mimalloc работает до двух раз быстрее на некоторых бенчмарках. В настоящее время использование mimalloc в Kotlin/Native экспериментально; вы можете переключиться на него, используя опцию компилятора
-Xallocator=mimalloc.Мы переработали способ сборки библиотек C-взаимодействия. С новым инструментом Kotlin/Native производит библиотеки взаимодействия до 4 раз быстрее, чем раньше, а артефакты составляют от 25% до 30% от своего первоначального размера.
Общая производительность во время выполнения улучшилась благодаря оптимизациям в GC. Это улучшение будет особенно заметно в проектах с большим количеством долгоживущих объектов.
HashMapиHashSetколлекции теперь работают быстрее благодаря устранению лишнего боксинга.В 1.3.70 мы добавили две новые функции для повышения производительности компиляции Kotlin/Native: кеширование зависимостей проекта и запуск компилятора из демона Gradle. С тех пор мы исправили многочисленные ошибки и улучшили общую стабильность этих функций.
Упрощённое управление зависимостями CocoaPods
Ранее, после интеграции вашего проекта с менеджером зависимостей CocoaPods, вы могли собирать iOS, macOS, watchOS или tvOS часть вашего проекта только в Xcode, отдельно от других частей вашего кроссплатформенного проекта. Эти другие части можно было собирать в IntelliJ IDEA.
Кроме того, каждый раз, когда вы добавляли зависимость от Objective-C библиотеки, хранящейся в CocoaPods (библиотека Pod), вам приходилось переключаться с IntelliJ IDEA на Xcode, вызывать pod install, и запускать сборку Xcode там.
Теперь вы можете управлять зависимостями Pod прямо в IntelliJ IDEA, наслаждаясь преимуществами, которые он предоставляет для работы с кодом, такими как подсветка синтаксиса и автодополнение. Вы также можете собрать весь Kotlin проект с помощью Gradle, не переключаясь на Xcode. Это означает, что вам нужно переходить в Xcode только тогда, когда вам нужно написать код Swift/Objective-C или запустить ваше приложение на эмуляторе или устройстве.
Теперь вы также можете работать с Pod библиотеками, хранящимися локально.
В зависимости от ваших потребностей, вы можете добавлять зависимости между:
Kotlin проектом и Pod библиотеками, хранящимися удалённо в репозитории CocoaPods или локально на вашем компьютере.
Kotlin Pod (Kotlin проект, используемый в качестве CocoaPods зависимости) и Xcode проектом с одним или несколькими целевыми приложениями.
Завершите начальную настройку, и когда вы добавите новую зависимость в cocoapods, просто повторно импортируйте проект в IntelliJ IDEA. Новая зависимость будет добавлена автоматически. Дополнительные шаги не требуются.
Kotlin Многоплатформенность
Kotlin Многоплатформенность сокращает время, затрачиваемое на написание и сопровождение одного и того же кода для разных платформ, сохраняя при этом гибкость и преимущества нативного программирования. Мы продолжаем инвестировать свои усилия в многоплатформенные функции и улучшения:
Общий код для нескольких целей с иерархической структурой проекта
Благодаря поддержке новой иерархической структуры проекта вы можете использовать общий код между несколькими платформами в многоплатформенном проекте.
Ранее любой код, добавленный в многоплатформенный проект, мог быть размещен либо в наборе исходного кода, специфичного для платформы, ограниченного одной целью и непереиспользуемого другой платформой, либо в общем наборе исходного кода, например, commonMain или commonTest, который используется на всех платформах в проекте. В общем наборе исходного кода вы могли вызывать API, специфичный для платформы, только с помощью expect объявления, которое требует платформа-специфических actual реализаций.
Это упрощало общий код на всех платформах, но не так просто было общий код только между некоторыми из целей, особенно между похожими, которые могли бы повторно использовать много общего логики и API сторонних разработчиков.
Например, в типичном многоплатформенном проекте, нацеленном на iOS, есть две цели, связанные с iOS: одна для устройств iOS ARM64 и другая для симулятора x64. У них есть отдельные наборы исходного кода, специфичные для платформы, но на практике редко возникает необходимость в разном коде для устройства и симулятора, а их зависимости очень похожи. Поэтому код, специфичный для iOS, мог бы использоваться между ними.
По всей видимости, в этой настройке было бы желательно иметь общий набор исходного кода для двух целей iOS с Kotlin/Native-кодом, который по-прежнему мог бы напрямую вызывать любые API, общие для устройства iOS и симулятора.
Теперь вы можете сделать это с помощью поддержки иерархической структуры проекта, которая выводит и адаптирует доступные API и языковые функции в каждом наборе исходного кода в зависимости от того, какие цели их потребляют.
Для распространенных комбинаций целей вы можете создать иерархическую структуру с ярлыками для целей.
Например, создайте две цели iOS и общий набор исходного кода, показанный выше, с ярлыком для целей ios().
kotlin {
ios() // iOS device and simulator targets; iosMain and iosTest source sets
}
Для других комбинаций целей создайте иерархию вручную, связав наборы исходного кода с отношением dependsOn.
kotlin{
sourceSets {
val desktopMain by creating {
dependsOn(commonMain)
}
val linuxX64Main by getting {
dependsOn(desktopMain)
}
val mingwX64Main by getting {
dependsOn(desktopMain)
}
val macosX64Main by getting {
dependsOn(desktopMain)
}
}
}
kotlin {
sourceSets {
desktopMain {
dependsOn(commonMain)
}
linuxX64Main {
dependsOn(desktopMain)
}
mingwX64Main {
dependsOn(desktopMain)
}
macosX64Main {
dependsOn(desktopMain)
}
}
}
Благодаря иерархической структуре проекта библиотеки также могут предоставлять общий API для подмножества целей. Узнайте больше о общем коде в библиотеках.
Использование нативных библиотек в иерархической структуре
Вы можете использовать зависящие от платформы библиотеки, такие как Foundation, UIKit и POSIX, в наборах исходного кода, общих для нескольких нативных целей. Это поможет вам использовать больше нативного кода, не ограничиваясь зависимостями, специфичными для платформы.
Дополнительные шаги не требуются — все выполняется автоматически. IntelliJ IDEA поможет вам определить общие объявления, которые вы можете использовать в общем коде.
Узнайте больше о применении библиотек, зависящих от платформы.
Указание зависимостей только один раз
Отныне вместо указания зависимостей от разных вариантов одной и той же библиотеки в общих и специфичных для платформы наборах исходного кода, где она используется, вы должны указать зависимость только один раз в общем наборе исходного кода.
kotlin {
sourceSets {
val commonMain by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1")
}
}
}
}
kotlin {
sourceSets {
commonMain {
dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1'
}
}
}
}
Не используйте имена артефактов библиотек kotlinx с суффиксами, указывающими платформу, такими как -common, -native, или аналогичными, так как они больше НЕ поддерживаются. Вместо этого используйте имя базового артефакта библиотеки, которое в приведенном выше примере — kotlinx-coroutines-core.
Однако это изменение пока не затрагивает:
Библиотеку
stdlib— начиная с Kotlin 1.4.0, зависимость от стандартной библиотеки добавляется автоматически.Библиотеку
kotlin.test— вы все еще должны использоватьtest-commonиtest-annotations-common. Эти зависимости будут рассмотрены позже.
Если вам нужна зависимость только для определенной платформы, вы по-прежнему можете использовать платформоспецифические варианты стандартных и kotlinx библиотек с такими суффиксами, как -jvm или -js, например, kotlinx-coroutines-core-jvm.
Улучшения проектов Gradle
Помимо функций и улучшений проектов Gradle, специфичных для Kotlin Multiplatform, Kotlin/JVM, Kotlin/Native и Kotlin/JS, есть несколько изменений, применимых ко всем проектам Kotlin Gradle:
Зависимость от стандартной библиотеки добавляется по умолчанию
Вам больше не нужно объявлять зависимость от библиотеки stdlib в любом проекте Kotlin Gradle, включая многоплатформенный. Зависимость добавляется по умолчанию.
Автоматически добавленная стандартная библиотека будет иметь ту же версию, что и плагин Kotlin Gradle, так как они имеют одинаковую систему версионирования.
Для наборов исходных кодов, специфичных для платформы, используется соответствующая платформа-специфичная версия библиотеки, а общая стандартная библиотека добавляется ко всем остальным. Плагин Kotlin Gradle выберет соответствующую стандартную библиотеку JVM в зависимости от kotlinOptions.jvmTarget опции компилятора вашего скрипта Gradle.
Минимальная версия Gradle для проектов Kotlin
Чтобы использовать новые функции в своих проектах Kotlin, обновите Gradle до последней версии. Многоплатформенные проекты требуют Gradle 6.0 или выше, а другие проекты Kotlin работают с Gradle 5.4 или выше.
Улучшенная поддержка *.gradle.kts в IDE
В версии 1.4.0 мы продолжили улучшение поддержки IDE для скриптов Gradle Kotlin DSL (*.gradle.kts файлы). Вот что приносит новая версия:
-
Явное загрузка конфигураций скриптов для повышения производительности. Ранее внесенные изменения в скрипт сборки загружались автоматически в фоновом режиме. Для повышения производительности в версии 1.4.0 мы отключили автоматическую загрузку конфигурации скрипта сборки. Теперь IDE загружает изменения только при явном их применении.
В версиях Gradle, предшествующих 6.0, вам необходимо вручную загрузить конфигурацию скрипта, нажав Загрузить конфигурацию в редакторе.

В Gradle 6.0 и выше вы можете явно применить изменения, нажав Загрузить изменения Gradle или повторно импортировав проект Gradle.
Мы добавили еще одно действие в IntelliJ IDEA 2020.1 с Gradle 6.0 и выше – Загрузить конфигурации скриптов, которое загружает изменения в конфигурации скриптов без обновления всего проекта. Это занимает гораздо меньше времени, чем повторный импорт всего проекта.
Вам также следует Загрузить конфигурации скриптов для вновь созданных скриптов или при первом открытии проекта с новым плагином Kotlin.
С Gradle 6.0 и выше вы можете загрузить все скрипты сразу, в отличие от предыдущей реализации, где они загружались по отдельности. Поскольку каждый запрос требует выполнения фазы конфигурации Gradle, это может быть ресурсоемким для крупных проектов Gradle.
В настоящее время такая загрузка ограничена
build.gradle.ktsиsettings.gradle.ktsфайлами (пожалуйста, проголосуйте за соответствующую задачу). Чтобы включить выделение дляinit.gradle.ktsили примененных скрипт-плагинов, используйте старый механизм – добавьте их в автономные скрипты. Конфигурация для этих скриптов будет загружена отдельно, когда это потребуется. Вы также можете включить автоматическую перезагрузку для таких скриптов.
Улучшенная обработка ошибок. Ранее вы могли видеть ошибки Gradle Daemon только в отдельных лог-файлах. Теперь Gradle Daemon возвращает всю информацию об ошибках напрямую и отображает ее в окне инструмента сборки. Это экономит ваше время и усилия.
Стандартная библиотека
Вот список наиболее значительных изменений в стандартной библиотеке Kotlin в версии 1.4.0:
Общий API обработки исключений
Следующие элементы API были перемещены в общую библиотеку:
Throwable.stackTraceToString()расширяющая функция, которая возвращает подробное описание этой ошибки с её трассировкой стека, иThrowable.printStackTrace(), которая выводит это описание в стандартный поток ошибок.Throwable.addSuppressed()функция, которая позволяет указать исключения, которые были подавлены для передачи исключения, иThrowable.suppressedExceptionsсвойство, которое возвращает список всех подавленных исключений.@Throwsаннотация, которая перечисляет типы исключений, которые будут проверены при компиляции функции в платформенный метод (на JVM или нативных платформах).
Новые функции для массивов и коллекций
Коллекции
В версии 1.4.0 стандартная библиотека включает ряд полезных функций для работы с коллекциями:
-
setOfNotNull(), которая создаёт множество, состоящее из всех значений, не равных null, среди предоставленных аргументов.fun main() { //sampleStart val set = setOfNotNull(null, 1, 2, 0, null) println(set) //sampleEnd } -
shuffled()для последовательностей.fun main() { //sampleStart val numbers = (0 until 50).asSequence() val result = numbers.map { it * 2 }.shuffled().take(5) println(result.toList()) //five random even numbers below 100 //sampleEnd } -
*Indexed()аналоги массивовonEach()иflatMap(). Операция, которую они применяют к элементам коллекции, имеет индекс элемента в качестве параметра.fun main() { //sampleStart listOf("a", "b", "c", "d").onEachIndexed { index, item -> println(index.toString() + ":" + item) } val list = listOf("hello", "kot", "lin", "world") val kotlin = list.flatMapIndexed { index, item -> if (index in 1..2) item.toList() else emptyList() } //sampleEnd println(kotlin) } -
*OrNull()аналогиrandomOrNull(),reduceOrNull(), иreduceIndexedOrNull(). Они возвращаютnullдля пустых коллекций.fun main() { //sampleStart val empty = emptyList<Int>() empty.reduceOrNull { a, b -> a + b } //empty.reduce { a, b -> a + b } // Exception: Empty collection can't be reduced. //sampleEnd } -
runningFold(), её синонимscan(), иrunningReduce()применяют заданную операцию к элементам коллекции последовательно, подобноfold()иreduce(); разница состоит в том, что эти новые функции возвращают всю последовательность промежуточных результатов.fun main() { //sampleStart val numbers = mutableListOf(0, 1, 2, 3, 4, 5) val runningReduceSum = numbers.runningReduce { sum, item -> sum + item } val runningFoldSum = numbers.runningFold(10) { sum, item -> sum + item } //sampleEnd println(runningReduceSum.toString()) println(runningFoldSum.toString()) } -
sumOf()принимает функцию-селектор и возвращает сумму её значений для всех элементов коллекции.sumOf()может вычислять суммы типовInt,Long,Double,UInt, иULong. На JVM также доступныBigIntegerиBigDecimal.data class OrderItem(val name: String, val price: Double, val count: Int) fun main() { //sampleStart val order = listOf<OrderItem>( OrderItem("Cake", price = 10.0, count = 1), OrderItem("Coffee", price = 2.5, count = 3), OrderItem("Tea", price = 1.5, count = 2)) val total = order.sumOf { it.price * it.count } // Double val count = order.sumOf { it.count } // Int //sampleEnd println("You've ordered $count items that cost $total in total") } Функции
min()иmax()были переименованы вminOrNull()иmaxOrNull()для соблюдения соглашения об именовании, используемого в API коллекций Kotlin. Суффикс*OrNullв имени функции означает, что она возвращаетnullесли коллекция-получатель пуста. То же самое относится кminBy(),maxBy(),minWith(),maxWith(), — в 1.4 они имеют*OrNull()синонимы.-
Новые расширяющие функции
minOf()иmaxOf()возвращают минимальное и максимальное значение заданной функции-селектора для элементов коллекции.data class OrderItem(val name: String, val price: Double, val count: Int) fun main() { //sampleStart val order = listOf<OrderItem>( OrderItem("Cake", price = 10.0, count = 1), OrderItem("Coffee", price = 2.5, count = 3), OrderItem("Tea", price = 1.5, count = 2)) val highestPrice = order.maxOf { it.price } //sampleEnd println("The most expensive item in the order costs $highestPrice") }Также существуют
minOfWith()иmaxOfWith(), которые принимаютComparatorв качестве аргумента, и*OrNull()версии всех четырёх функций, которые возвращаютnullдля пустых коллекций. -
Новые перегрузки для
flatMapиflatMapToпозволяют использовать преобразования с типами возвращаемых значений, которые не совпадают с типом получателя, а именно:Преобразования в
SequenceдляIterable,Array, иMapПреобразования в
IterableдляSequence
fun main() { //sampleStart val list = listOf("kot", "lin") val lettersList = list.flatMap { it.asSequence() } val lettersSeq = list.asSequence().flatMap { it.toList() } //sampleEnd println(lettersList) println(lettersSeq.toList()) } removeFirst()иremoveLast()сокращения для удаления элементов из изменяемых списков, а также*orNull()аналоги этих функций.
Массивы
Для обеспечения согласованного опыта при работе с различными типами контейнеров, мы также добавили новые функции для массивов:
shuffle()перемещает элементы массива в случайном порядке.onEach()выполняет заданное действие над каждым элементом массива и возвращает сам массив.associateWith()иassociateWithTo()создают карты с элементами массива в качестве ключей.reverse()для поддиапазонов массивов изменяет порядок элементов в поддиапазоне.sortDescending()для поддиапазонов массивов сортирует элементы в поддиапазоне в порядке убывания.sort()иsortWith()для поддиапазонов массивов теперь доступны в общей библиотеке.
fun main() {
//sampleStart
var language = ""
val letters = arrayOf("k", "o", "t", "l", "i", "n")
val fileExt = letters.onEach { language += it }
.filterNot { it in "aeuio" }.take(2)
.joinToString(prefix = ".", separator = "")
println(language) // "kotlin"
println(fileExt) // ".kt"
letters.shuffle()
letters.reverse(0, 3)
letters.sortDescending(2, 5)
println(letters.contentToString()) // [k, o, t, l, i, n]
//sampleEnd
}
Кроме того, существуют новые функции для преобразований между CharArray/ByteArray и String:
ByteArray.decodeToString()иString.encodeToByteArray()CharArray.concatToString()иString.toCharArray()
fun main() {
//sampleStart
val str = "kotlin"
val array = str.toCharArray()
println(array.concatToString())
//sampleEnd
}
ArrayDeque
Мы также добавили класс ArrayDeque — реализацию двусторонней очереди. Двусторонняя очередь позволяет добавлять или удалять элементы в начале или конце очереди за амортизированное постоянное время. По умолчанию вы можете использовать двустороннюю очередь, когда в вашем коде требуется очередь или стек.
fun main() {
val deque = ArrayDeque(listOf(1, 2, 3))
deque.addFirst(0)
deque.addLast(4)
println(deque) // [0, 1, 2, 3, 4]
println(deque.first()) // 0
println(deque.last()) // 4
deque.removeFirst()
deque.removeLast()
println(deque) // [1, 2, 3]
}
Реализация ArrayDeque использует изменяемый массив в качестве основы: она хранит содержимое в циклическом буфере, Array, и изменяет размер этого Array только когда он заполняется.
Функции для обработки строк
В стандартной библиотеке 1.4.0 имеется ряд улучшений в API для обработки строк:
-
StringBuilderимеет новые полезные расширяющие функции:set(),setRange(),deleteAt(),deleteRange(),appendRange(), и другие.fun main() { //sampleStart val sb = StringBuilder("Bye Kotlin 1.3.72") sb.deleteRange(0, 3) sb.insertRange(0, "Hello", 0 ,5) sb.set(15, '4') sb.setRange(17, 19, "0") print(sb.toString()) //sampleEnd } Некоторые существующие функции
StringBuilderдоступны в общей библиотеке. Среди нихappend(),insert(),substring(),setLength(), и другие.-
Новые функции
Appendable.appendLine()иStringBuilder.appendLine()были добавлены в общую библиотеку. Они заменяют функцииappendln()только для JVM этих классов.fun main() { //sampleStart println(buildString { appendLine("Hello,") appendLine("world") }) //sampleEnd }
Битовые операции
Новые функции для битовых манипуляций:
countOneBits()countLeadingZeroBits()countTrailingZeroBits()takeHighestOneBit()takeLowestOneBit()rotateLeft()иrotateRight()(экспериментально)
fun main() {
//sampleStart
val number = "1010000".toInt(radix = 2)
println(number.countOneBits())
println(number.countTrailingZeroBits())
println(number.takeHighestOneBit().toString(2))
//sampleEnd
}
Улучшения делегированных свойств
В версии 1.4.0 мы добавили новые возможности, чтобы улучшить ваш опыт работы с делегированными свойствами в Kotlin:
Теперь свойство можно делегировать другому свойству.
Новый интерфейс
PropertyDelegateProviderпомогает создавать провайдеры делегатов в одном объявлении.ReadWritePropertyтеперь расширяетReadOnlyProperty, поэтому вы можете использовать оба для свойств только для чтения.
Помимо нового API, мы внесли некоторые оптимизации, которые уменьшают размер результирующего байткода. Эти оптимизации описаны в этой статье блога.
Преобразование из KType в тип Java
Новое расширяющее свойство KType.javaType (в настоящее время экспериментальное) в стандартной библиотеке поможет вам получить тип java.lang.reflect.Type из типа Kotlin без использования всей зависимости kotlin-reflect.
import kotlin.reflect.javaType
import kotlin.reflect.typeOf
@OptIn(ExperimentalStdlibApi::class)
inline fun <reified T> accessReifiedTypeArg() {
val kType = typeOf<T>()
println("Kotlin type: $kType")
println("Java type: ${kType.javaType}")
}
@OptIn(ExperimentalStdlibApi::class)
fun main() {
accessReifiedTypeArg<String>()
// Kotlin type: kotlin.String
// Java type: class java.lang.String
accessReifiedTypeArg<List<String>>()
// Kotlin type: kotlin.collections.List<kotlin.String>
// Java type: java.util.List<java.lang.String>
}
Настройки Proguard для рефлексии Kotlin
Начиная с версии 1.4.0, мы интегрировали настройки Proguard/R8 для рефлексии Kotlin в kotlin-reflect.jar. Благодаря этому большинство проектов Android, использующих R8 или Proguard, должны работать с kotlin-reflect без дополнительных настроек. Вам больше не нужно копировать правила Proguard для внутренних компонентов kotlin-reflect. Однако обратите внимание, что вам по-прежнему необходимо явно указать все API, которые вы собираетесь отражать.
Улучшение существующего API
-
Несколько функций теперь работают с нулевыми получателями, например:
toBoolean()для строкcontentEquals(),contentHashcode(),contentToString()для массивов
NaN,NEGATIVE_INFINITY, иPOSITIVE_INFINITYвDoubleиFloatтеперь определены какconst, поэтому вы можете использовать их в качестве аргументов аннотаций.Новые константы
SIZE_BITSиSIZE_BYTESвDoubleиFloatсодержат количество битов и байтов, используемых для представления экземпляра типа в двоичной форме.Функции
maxOf()иminOf()верхнего уровня теперь могут принимать переменное количество аргументов (vararg).
Описатели module-info для артефактов stdlib
Kotlin 1.4.0 добавляет описатели информации о модулях module-info.java к стандартным библиотечным артефактам. Это позволяет использовать их с инструментом jlink, который генерирует собственные образы среды выполнения Java, содержащие только те платформенные модули, которые требуются вашему приложению. Вы уже могли использовать jlink с артефактами стандартной библиотеки Kotlin, но для этого вам нужно было использовать отдельные артефакты (с классификатором "modular") и вся настройка была нетривиальной. В Android убедитесь, что вы используете плагин Android Gradle версии 3.2 или выше, который может правильно обрабатывать файлы JAR с module-info.
Устаревшие элементы
toShort() и toByte() для Double и Float
Мы устарели функции toShort() и toByte() для Double и Float, так как они могут приводить к непредсказуемым результатам из-за узкого диапазона значений и меньшего размера переменной.
Чтобы преобразовать числа с плавающей точкой в Byte или Short, используйте двухэтапное преобразование: сначала преобразуйте их в Int, а затем снова преобразуйте в целевой тип.
contains(), indexOf() и lastIndexOf() для массивов чисел с плавающей точкой
Мы устарели функции contains(), indexOf(), и lastIndexOf() расширений для FloatArray и DoubleArray, так как они используют стандарт IEEE 754 сравнения, что противоречит равенству полного порядка в некоторых граничных случаях. Подробнее см. данную проблему.
Функции min() и max() для коллекций
Мы устарели функции min() и max() для коллекций в пользу minOrNull() и maxOrNull(), которые более точно отражают их поведение — возвращают null для пустых коллекций. Подробнее см. данную проблему.
Исключение устаревшего экспериментального корутин
API kotlin.coroutines.experimental был устаревшим в пользу kotlin.coroutines в версии 1.3.0. В версии 1.4.0 мы завершаем цикл устаревания kotlin.coroutines.experimental путем удаления его из стандартной библиотеки. Для тех, кто по-прежнему использует его на JVM, мы предоставили совместимый артефакт kotlin-coroutines-experimental-compat.jar со всеми API экспериментальных корутин. Мы опубликовали его в Maven, и он включен в дистрибутив Kotlin наряду со стандартной библиотекой.
Стабильная сериализация JSON
С Kotlin 1.4.0 мы поставляем первую стабильную версию kotlinx.serialization - 1.0.0-RC. Теперь мы объявляем API сериализации JSON в kotlinx-serialization-core (ранее известный как kotlinx-serialization-runtime) стабильным. Библиотеки для других форматов сериализации остаются экспериментальными, вместе с некоторыми расширенными частями основной библиотеки.
Мы значительно переработали API для сериализации JSON, чтобы сделать его более последовательным и удобным в использовании. Отныне мы будем продолжать развивать API сериализации JSON в обратной совместимости. Однако, если вы использовали предыдущие версии, вам потребуется переписать часть своего кода при переходе на версию 1.0.0-RC. Чтобы помочь вам с этим, мы также предлагаем Руководство по сериализации Kotlin – полное руководство по kotlinx.serialization. Оно проведет вас через процесс использования наиболее важных функций и поможет решить любые проблемы, с которыми вы можете столкнуться.
Сценарии и REPL
В версии 1.4.0 сценарии на Kotlin получили ряд функциональных и производительных улучшений, а также другие обновления. Вот некоторые ключевые изменения:
Чтобы помочь вам лучше освоиться со сценариями на Kotlin, мы подготовили проект с примерами на GitHub. Он содержит примеры стандартных сценариев (*.main.kts) и примеры использования API Kotlin Scripting и пользовательских определений сценариев. Пожалуйста, попробуйте и поделитесь своими отзывами с помощью нашего трекера задач.
Новый API разрешения зависимостей
В версии 1.4.0 мы представили новый API для разрешения внешних зависимостей (например, артефактов Maven), а также реализации для него. Этот API опубликован в новых артефактах kotlin-scripting-dependencies и kotlin-scripting-dependencies-maven. Предыдущая функциональность разрешения зависимостей в библиотеке kotlin-script-util теперь устарела.
Новый API REPL
Новый экспериментальный API REPL теперь является частью API Kotlin Scripting. Существует также несколько реализаций его в опубликованных артефактах, некоторые из которых обладают расширенной функциональностью, такой как автодополнение кода. Мы используем этот API в ядре Kotlin Jupyter, и теперь вы можете попробовать его в собственных пользовательских оболочках и REPL.
Кэш скомпилированных сценариев
API Kotlin Scripting теперь предоставляет возможность реализовать кэш скомпилированных сценариев, что значительно ускоряет последующие выполнения неизменённых сценариев. Наша стандартная расширенная реализация сценариев kotlin-main-kts уже имеет свой собственный кэш.
Переименование артефактов
Для избежания путаницы с именами артефактов мы переименовали kotlin-scripting-jsr223-embeddable и kotlin-scripting-jvm-host-embeddable в просто kotlin-scripting-jsr223 и kotlin-scripting-jvm-host. Эти артефакты зависят от артефакта kotlin-compiler-embeddable, который затемняет связанные сторонние библиотеки, чтобы избежать конфликтов в использовании. С этим переименованием мы делаем использование kotlin-compiler-embeddable (что, как правило, безопаснее) стандартным для сценариев-артефактов. Если по какой-то причине вам нужны артефакты, зависящие от незатемнённого kotlin-compiler, используйте версии артефактов с суффиксом -unshaded, например, kotlin-scripting-jsr223-unshaded. Обратите внимание, что это переименование затрагивает только сценарий-артефакты, которые предполагается использовать напрямую; имена других артефактов остаются неизменными.
Миграция на Kotlin 1.4.0
Инструменты миграции плагина Kotlin помогут вам мигрировать ваши проекты из более ранних версий Kotlin в 1.4.0.
Просто измените версию Kotlin на 1.4.0 и повторно импортируйте свой проект Gradle или Maven. Затем IDE спросит вас о миграции.
Если вы согласитесь, она запустит проверки кода миграции, которые проверят ваш код и предложат исправления для всего, что не работает или не рекомендуется в 1.4.0.
У проверок кода есть разные уровни строгости, чтобы помочь вам решить, какие предложения принять, а какие игнорировать.
Kotlin 1.4.0 является выпуском новых функций и, следовательно, может внести несовместимые изменения в язык. Подробный список таких изменений можно найти в Руководстве по совместимости для Kotlin 1.4.
© 2010–2023 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/whatsnew14.html