Что нового в 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 , включенных в циклы. Причина заключалась в том, что эти ключевые слова были зарезервированы для возможного поведения просеивания в выражениях 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'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) и сохранение (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, а также поддержка разработки, зависимостей и необязательных зависимостей 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 мы немного изменили 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, считаются необработанными и приводят к завершению программы.
Генерация .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% по размеру.
Общая производительность выполнения улучшилась благодаря оптимизациям в сборщике мусора. Это улучшение будет особенно заметно в проектах с большим количеством долгоживущих объектов.
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 сокращает время, затраченное на написание и поддержку одинакового кода для разных платформ, сохраняя при этом гибкость и преимущества нативной разработки. Мы продолжаем инвестировать усилия в функции и улучшения multiplatform:
Обмен кодом для нескольких целей с иерархической структурой проекта
С поддержкой новой иерархической структуры проекта вы можете обмениваться кодом между несколькими платформами в проекте multiplatform.
Ранее любой код, добавленный в проект multiplatform, мог быть размещён либо в наборе исходных файлов, специфичных для платформы, что ограничено одной целью и не может быть повторно использовано другой платформой, либо в общем наборе исходных файлов, например, commonMain или commonTest, который используется на всех платформах проекта. В общем наборе исходных файлов вы могли вызывать API, специфичные для платформы, только с использованием expect объявления, требующего actual реализации, специфичных для платформы.
Это облегчало обмен кодом на всех платформах, но не так легко было обмениваться кодом только между некоторыми целями, особенно похожими, которые потенциально могли бы повторно использовать значительную часть общей логики и API сторонних поставщиков.
Например, в типичном проекте multiplatform, нацеленном на 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, зависимость от stdlib добавляется автоматически.Библиотеку
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 Collections. Суффикс*OrNullв имени функции означает, что она возвращаетnullесли коллекция-приёмник пустая. То же самое относится кminBy(),maxBy(),minWith(),maxWith(), - у них появились*OrNull()синонимы в версии 1.4.-
Новые расширяющие функции
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 для артефактов стандартной библиотеки
Kotlin 1.4.0 добавляет информацию о модулях module-info.java в стандартные артефакты стандартной библиотеки. Это позволяет использовать их с инструментом jlink, который генерирует собственные Java-среды исполнения, содержащие только платформенные модули, необходимые для вашего приложения. Вы уже могли использовать jlink с артефактами Kotlin стандартной библиотеки, но для этого нужно было использовать отдельные артефакты с классификатором "modular", а вся настройка не была простой. В 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, мы подготовили проект с примерами. Он содержит примеры стандартных скриптов (*.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 в опубликованных артефактах, и некоторые из них имеют расширенную функциональность, например, автодополнение кода. Мы используем этот 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–2022 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/whatsnew14.html