Что нового в Kotlin 1.4
Дата релиза: 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)}")
}
Узнайте больше о функциональных интерфейсах Kotlin и преобразованиях SAM.
Режим явного 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).
Это было действительно раздражающе, когда все аргументы были в правильных позициях, но вы хотели указать имя для одного аргумента посередине. Это было особенно полезно для явного указания, к какому атрибуту относится булево или 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 , включенных в циклы. Причиной было то, что эти ключевые слова были зарезервированы для возможного поведения fall-through в выражениях 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)
}
}
}
Поведение fall-through внутри выражений 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. Вот некоторые из самых заметных улучшений:
Больше случаев, когда тип выводится автоматически
Новый алгоритм вывода типов выводит типы во многих случаях, когда старый алгоритм требовал явного указания. Например, в следующем примере тип параметра лямбды 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's default методы. Для этого существовал механизм, который включает аннотацию @JvmDefault для маркировки таких методов и параметр компилятора -Xjvm-default для включения обработки этой аннотации.
В версии 1.4.0 мы добавили новый режим генерации методов по умолчанию: -Xjvm-default=all компилирует все неабстрактные методы интерфейсов Kotlin в default методы Java. Для совместимости с кодом, использующим интерфейсы, скомпилированные без 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-компилятором, либо различными инструментами обработки байткода, такими как оптимизатор Android R8.
Обратите внимание, что с точки зрения разработчика, изменения будут незначительны: код Kotlin будет выбрасывать исключения с теми же сообщениями об ошибках, что и раньше. Тип исключения меняется, но передаваемая информация остаётся той же.
Аннотации типов в байткоде JVM
Kotlin теперь может генерировать аннотации типов в байткоде JVM (целевая версия 1.8+), так что они будут доступны в Java-рефлексии во время выполнения. Чтобы сгенерировать аннотацию типа в байткоде, выполните следующие шаги:
Убедитесь, что ваша объявленная аннотация имеет соответствующую метку для целевого объекта аннотации (Java's
ElementType.TYPE_USEили Kotlin'sAnnotationTarget.TYPE) и retention (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 предоставляет следующие улучшения:
Новый DSL Gradle
Плагин Gradle kotlin.js поставляется с изменённым DSL Gradle, который предоставляет ряд новых параметров конфигурации и более тесно интегрирован с DSL плагина kotlin-multiplatform. Некоторые из наиболее значительных изменений включают:
Явные переключатели для создания исполняемых файлов через
binaries.executable(). Подробнее о выполнении Kotlin/JS и его среде читайте здесь.Настройка загрузчиков CSS и стилей webpack из конфигурации Gradle через
cssSupport. Подробнее о использовании загрузчиков CSS и стилей.Улучшенное управление зависимостями npm, с обязательными версиями или диапазонами версий semver, а также поддержка разработки, зависимостей peer, и необязательных зависимостей 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
В версии 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 мы немного изменили API Swift, сгенерированный из 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 по умолчанию генерирует файлы отладочных символов (.dSYM для релизных бинарников на платформах 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 работает на 20% быстрее на некоторых бенчмарках. В настоящее время использование mimalloc в Kotlin/Native является экспериментальным; вы можете переключиться на него, используя опцию компилятора
-Xallocator=mimalloc.Мы переработали способ построения библиотек C взаимодействия. С новым инструментом, Kotlin/Native генерирует библиотеки взаимодействия до 4 раз быстрее, чем раньше, а артефакты имеют размер на 25-30% меньше, чем раньше.
Общий производительность времени выполнения улучшился благодаря оптимизациям в сборщике мусора. Это улучшение будет особенно заметно в проектах с большим количеством долгоживущих объектов.
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 Multiplatform
Kotlin Multiplatform сокращает время, затрачиваемое на написание и поддержку одинакового кода для разных платформ, сохраняя при этом гибкость и преимущества нативной разработки. Мы продолжаем инвестировать усилия в многоплатформенные функции и улучшения:
Использование кода на нескольких целевых платформах с иерархической структурой проекта
С новой поддержкой иерархической структуры проекта вы можете использовать код на нескольких платформах в многоплатформенном проекте.
Ранее любой код, добавленный в многоплатформенный проект, мог быть размещен либо в наборе исходных файлов, специфичных для платформы, который ограничен одной целевой платформой и не может быть повторно использован другой платформой, либо в общем наборе исходных файлов, таком как 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.6.4")
}
}
}
}
kotlin {
sourceSets {
commonMain {
dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'
}
}
}
}
Не используйте имена артефактов библиотек 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 до последней версии. Проекты Multiplatform требуют 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()добавлены в общую библиотеку. Они заменяют JVM-специфичныеappendln()функции этих классов.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 Type
Новое расширяемое свойство 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 Reflection в 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, но для этого вам нужно было использовать отдельные артефакты — те, которые имеют классификатор «модульный» — и вся настройка не была простой. В Android убедитесь, что вы используете версию Android Gradle Plugin 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 и пользовательских определений скриптов. Пожалуйста, попробуйте и поделитесь своими отзывами, используя наш отслеживатель задач.
Новый API для разрешения зависимостей
В версии 1.4.0 мы представили новый API для разрешения внешних зависимостей (например, артефактов Maven), а также реализации для него. Этот API опубликован в новых артефактах kotlin-scripting-dependencies и kotlin-scripting-dependencies-maven. Предыдущая функциональность разрешения зависимостей в библиотеке kotlin-script-util теперь устарела.
Новый API REPL
Новый экспериментальный API REPL теперь является частью API для скриптов Kotlin. Также есть несколько его реализаций в опубликованных артефактах, некоторые из которых обладают расширенной функциональностью, такой как автодополнение кода. Мы используем этот API в ядренном модуле Kotlin Jupyter, и теперь вы можете использовать его в собственных пользовательских оболочках и REPL.
Кэш скомпилированных скриптов
API для скриптов Kotlin теперь предоставляет возможность реализации кэша скомпилированных скриптов, значительно ускоряя последующие выполнение неизмененных скриптов. Наша стандартная расширенная реализация скриптов 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