Spec-Zone.ru › Kotlin 1.6

Что нового в Kotlin 1.4

Дата выхода: 17 августа 2020

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

Функции языка и улучшения

Kotlin 1.4.0 поставляется с различными функциями языка и улучшениями. Они включают:

  • Преобразования SAM для интерфейсов Kotlin

  • Явный режим API для авторов библиотек

  • Смешивание именованных и позиционных аргументов

  • Завершающая запятая

  • Улучшения вызываемых ссылок

  • Использование break и continue внутри выражений when, включённых в циклы

Преобразования 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}

Дополнительные сведения о явном режиме API см. в KEEP.

Смешивание именованных и позиционных аргументов

В 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 Project Wizard – Multiplatform project

Новый мастер проектов Kotlin прост и гибок:

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

  2. Выберите систему сборки – Gradle (Kotlin или Groovy DSL), Maven или IntelliJ IDEA.
    Мастер проектов Kotlin отобразит только поддерживаемые системы сборки для выбранного шаблона проекта.

  3. Предпросмотр структуры проекта непосредственно на главном экране.

Затем вы можете завершить создание проекта или, по желанию, настроить проект на следующем экране:

  1. Добавить/удалить модули и целевые платформы, поддерживаемые данным шаблоном проекта.

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

Kotlin Project Wizard - Configure targets

В будущем мы планируем сделать мастер проектов Kotlin еще более гибким, добавив больше параметров настройки и шаблонов.

Вы можете опробовать новый мастер проектов Kotlin, выполнив эти учебные пособия:

  • Создание консольного приложения на основе Kotlin/JVM

  • Создание приложения Kotlin/JS для React

  • Создание приложения Kotlin/Native

Отладчик сопрограмм

Многие уже используют сопрограммы для асинхронного программирования. Но отладка с сопрограммами до Kotlin 1.4 была достаточно сложной. Так как сопрограммы переключались между потоками, было сложно понять, что делает конкретная сопрограмма и проверить её контекст. В некоторых случаях отслеживание шагов через точки останова просто не работало. В результате приходилось полагаться на логирование или умственные усилия для отладки кода, использующего сопрограммы.

В Kotlin 1.4 отладка сопрограмм стала намного удобнее благодаря новым возможностям, добавленным в плагин Kotlin.

Отладка работает для версий 1.3.8 или выше kotlinx-coroutines-core.

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

Debugging coroutines

Теперь вы можете:

  • Легко проверять состояние каждой сопрограммы.

  • Просматривать значения локальных и захваченных переменных для работающих и приостановленных сопрограмм.

  • Просматривать полный стек создания сопрограммы, а также стек вызовов внутри сопрограммы. Стек включает все фреймы с значениями переменных, даже те, которые теряются при стандартной отладке.

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

Coroutines Dump

Узнайте больше об отладке сопрограмм в этой статье блога и документации IntelliJ IDEA.

Новый компилятор

Новый компилятор Kotlin будет очень быстрым; он объединит все поддерживаемые платформы и предоставит API для расширений компилятора. Это долгосрочный проект, и мы уже выполнили несколько шагов в Kotlin 1.4.0:

  • Новый, более мощный алгоритм вывода типов включен по умолчанию.

  • Новые бэкэнды JVM и JS IR. Они станут стандартными после их стабилизации.

Новый более мощный алгоритм вывода типов

Kotlin 1.4 использует новый, более мощный алгоритм вывода типов. Этот новый алгоритм уже был доступен для тестирования в Kotlin 1.3 путем указания параметра компилятора, а теперь используется по умолчанию. Полный список исправленных проблем в новом алгоритме можно найти в YouTrack. Ниже приведены некоторые из наиболее заметных улучшений:

  • Больше случаев, когда тип выводится автоматически

  • Умные преобразования типов для последнего выражения лямбда-выражения

  • Умные преобразования типов для вызываемых ссылок

  • Улучшенный вывод типов для делегированных свойств

  • Преобразование SAM для Java-интерфейсов с разными аргументами

  • Java SAM-интерфейсы в Kotlin

Больше случаев, когда тип выводится автоматически

Новый алгоритм вывода типов выводит типы для многих случаев, где старый алгоритм требовал явного указания. Например, в следующем примере тип параметра лямбда-выражения 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, которые сейчас в альфа-версии, и поделиться с нами своими отзывами.

END_OF_DOCUMENT_MARKER

Kotlin/JVM

Kotlin 1.4.0 включает ряд улучшений, специфичных для JVM, таких как:

  • Новый бэкенд JVM IR

  • Новые режимы генерации методов по умолчанию в интерфейсах

  • Единый тип исключения для проверок на null

  • Аннотации типов в байткоде 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

Если вы включите Jetpack Compose, вы автоматически перейдёте к новому бэкенду JVM без необходимости указывать опцию компилятора в kotlinOptions.

При использовании компилятора из командной строки, добавьте параметр компилятора -Xuse-ir.

Вы можете использовать код, скомпилированный новым бэкендом JVM 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-рефлексии во время выполнения. Чтобы сгенерировать аннотацию типа в байткоде, выполните следующие шаги:

  1. Убедитесь, что ваша объявленная аннотация имеет правильную метку целевого объекта (Java’s ElementType.TYPE_USE или Kotlin’s AnnotationTarget.TYPE) и удержание (AnnotationRetention.RUNTIME).

  2. Скомпилируйте объявление класса аннотации в байт-код JVM с целевой версией 1.8+. Вы можете указать её параметром компилятора -jvm-target=1.8.

  3. Скомпилируйте код, использующий аннотацию, в байт-код JVM с целевой версией 1.8+ (-jvm-target=1.8) и добавьте параметр компилятора -Xemit-jvm-type-annotations.

Обратите внимание, что аннотации типов из стандартной библиотеки пока не генерируются в байткоде, так как стандартная библиотека скомпилирована с целевой версией 1.6.

На данный момент поддерживаются только базовые случаи:

  • Аннотации типа на параметрах метода, возвращаемых типах методов и типах свойств;

  • Инвариантные проекции аргументов типа, такие как Smth<@Ann Foo>, Array<@Ann Foo>.

В следующем примере аннотация @Foo на типе String может быть сгенерирована в байткоде и затем использована кодом библиотеки:

@Target(AnnotationTarget.TYPE)
annotation class Foo

class A {
    fun foo(): @Foo String = "OK"
}

Kotlin/JS

На платформе JS Kotlin 1.4.0 предоставляет следующие улучшения:

  • Новая Gradle DSL

  • Новый бэкенд JS IR

Новая Gradle DSL

Плагин Gradle kotlin.js поставляется с измененной Gradle DSL, которая предоставляет ряд новых опций конфигурации и более тесно интегрирована с DSL, используемой плагином kotlin-multiplatform. Некоторые из наиболее значительных изменений включают:

  • Явные переключатели для создания исполняемых файлов через binaries.executable(). Подробнее о запуске Kotlin/JS и его среде можно узнать здесь.

  • Конфигурирование CSS и стилей загрузчиков webpack из конфигурации Gradle с помощью cssSupport. Подробнее о использовании CSS и загрузчиков стилей можно узнать здесь.

  • Улучшенное управление зависимостями npm, с обязательными номерами версий или диапазонами версий semver, а также поддержка зависимостей npm разработки, peer и optional с использованием devNpm, optionalNpm и peerNpm. Подробнее о управлении зависимостями пакетов npm непосредственно из Gradle можно узнать здесь.

  • Более сильная интеграция с Dukat, генератором внешних объявлений Kotlin. Внешние объявления теперь можно генерировать во время сборки или вручную с помощью задачи Gradle. Подробнее об интеграции можно узнать здесь.

Новый бэкенд JS IR

Бэкенд IR для Kotlin/JS, который в настоящее время имеет стабильность Alpha, предоставляет новые функции, специфичные для цели 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

  • Поддержка Objective-C дженериков по умолчанию

  • Обработка исключений в Objective-C/Swift взаимодействии

  • Генерация release .dSYMs на целевых платформах Apple по умолчанию

  • Улучшения производительности

  • Упрощенное управление зависимостями CocoaPods

Поддержка приостанавливаемых функций 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 по умолчанию генерирует файлы символьных данных отладки (.dSYMs) для релизных бинарников на платформах Darwin. Это можно отключить с помощью опции компилятора -Xadd-light-debug=disable. На других платформах эта опция отключена по умолчанию. Чтобы переключить эту опцию в Gradle, используйте:

kotlin {
    targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget> {
        binaries.all {
            freeCompilerArgs += "-Xadd-light-debug={enable|disable}"
        }
    }
}

Подробнее о символизации отчетов об ошибках.

Улучшения производительности

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

  • Для повышения скорости выделения объектов мы теперь предлагаем mimalloc как альтернативу системному аллокатору. mimalloc работает до двух раз быстрее на некоторых тестах. В настоящее время использование mimalloc в Kotlin/Native экспериментальное; вы можете переключиться на него с помощью опции компилятора -Xallocator=mimalloc.

  • Мы переработали способ построения библиотек C-взаимодействия. С новым инструментом Kotlin/Native генерирует библиотеки взаимодействия до 4 раз быстрее, чем раньше, а артефакты меньше на 25–30%.

  • Общую производительность среды выполнения улучшили благодаря оптимизациям в GC. Это улучшение будет особенно заметно в проектах с большим количеством долгоживущих объектов. HashMap и HashSet коллекции теперь работают быстрее, избегая излишнего боксрования.

  • В 1.3.70 мы добавили две новые функции для повышения производительности компиляции Kotlin/Native: кеширование зависимостей проекта и запуск компилятора из демона Gradle. С тех пор мы устранили много проблем и улучшили общую стабильность этих функций.

Упрощенное управление зависимостями CocoaPods

Ранее, после интеграции проекта с менеджером зависимостей CocoaPods, можно было скомпилировать iOS, macOS, watchOS или tvOS часть проекта только в Xcode, отдельно от других частей вашего кроссплатформенного проекта. Остальные части можно было компилировать в Intellij IDEA.

Кроме того, каждый раз, когда вы добавляли зависимость от Objective-C библиотеки, хранящейся в CocoaPods (Pod библиотека), вам приходилось переключаться из IntelliJ IDEA в Xcode, вызывать pod install, и запускать там сборку Xcode.

Теперь вы можете управлять зависимостями Pod прямо в Intellij IDEA, получая преимущества, которые она предоставляет для работы с кодом, такие как подсветка синтаксиса и автодополнение. Также вы можете скомпилировать весь проект Kotlin с помощью Gradle, без переключения на Xcode. Это означает, что вам нужно переходить в Xcode только тогда, когда вам нужно писать код Swift/Objective-C или запускать ваше приложение на симуляторе или устройстве.

Теперь вы также можете работать с Pod библиотеками, хранящимися локально.

В зависимости от ваших потребностей, вы можете добавить зависимости между:

  • Проектом Kotlin и Pod библиотеками, хранящимися удаленно в репозитории CocoaPods или локально на вашем компьютере.

  • Kotlin Pod (проект Kotlin, используемый как зависимость CocoaPods) и проектом Xcode с одним или несколькими целевыми платформами.

Завершите начальную настройку, и когда вы добавите новую зависимость в cocoapods, просто повторно импортируйте проект в IntelliJ IDEA. Новая зависимость будет добавлена автоматически. Дополнительные шаги не требуются.

Узнайте, как добавить зависимости.

Kotlin Multiplatform

Поддержка проектов multiplatform находится на стадии альфа. Она может быть изменена несовместимо и в будущем потребовать ручную миграцию. Мы ценим ваши отзывы об этом на YouTrack.

Kotlin Multiplatform сокращает время, затраченное на написание и поддержку одинакового кода для различных платформ, сохраняя гибкость и преимущества нативного программирования. Мы продолжаем инвестировать усилия в возможности и улучшения multiplatform:

  • Общий код для нескольких целей с иерархической структурой проекта

  • Использование нативных библиотек в иерархической структуре

  • Указание зависимостей kotlinx только один раз

Проекты Multiplatform требуют Gradle 6.0 или более поздней версии.

Общий код для нескольких целей с иерархической структурой проекта

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

Ранее любой код, добавленный в проект multiplatform, мог быть размещён либо в наборе исходного кода, специфичном для платформы, который ограничен одной целью и не может быть повторно использован другой платформой, либо в общем наборе исходного кода, например commonMain или commonTest, который используется всеми платформами в проекте. В общем наборе исходного кода вы могли вызывать API, специфичный для платформы, только с помощью expect объявления, требующего платформоспецифических actual реализаций.

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

Например, в типичном проекте multiplatform, нацеленном на iOS, есть две связанные с iOS цели: одна для устройств iOS ARM64, а другая для симулятора x64. У них есть отдельные наборы исходного кода, специфичные для платформы, но на практике редко возникает необходимость в разном коде для устройства и симулятора, а их зависимости очень похожи. Поэтому код, специфичный для iOS, может быть использован для обоих.

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

Code shared for iOS targets

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

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

Например, создайте две цели iOS и общий набор исходного кода, показанный выше, со сокращением ios().

kotlin {
    ios() // iOS device and simulator targets; iosMain and iosTest source sets
}

Для других комбинаций целей, соединив наборы исходного кода с отношением dependsOn.

Hierarchical structure
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.0")
            }
        }
    }
}
kotlin {
    sourceSets {
        commonMain {
            dependencies {
                implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0'
            }
        }
    }
}

Не используйте имена артефактов библиотек 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:

  • Зависимость от стандартной библиотеки теперь добавляется по умолчанию

  • Для проектов Kotlin требуется недавняя версия Gradle

  • Улучшенная поддержка Kotlin Gradle DSL в IDE

Зависимость от стандартной библиотеки добавляется по умолчанию

Вам больше не нужно объявлять зависимость от библиотеки 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.kts – Load Configuration

    В Gradle 6.0 и выше вы можете явно применить изменения, нажав Загрузить изменения Gradle или перезагрузив проект Gradle.

    Мы добавили еще одно действие в IntelliJ IDEA 2020.1 с Gradle 6.0 и выше — Загрузить конфигурации скриптов, которое загружает изменения в конфигурации скриптов без обновления всего проекта. Это занимает намного меньше времени, чем перезагрузка всего проекта.

    *.gradle.kts – Load Script Changes and Load Gradle Changes

    Вы также должны Загрузить конфигурации скриптов для новых скриптов или при первом открытии проекта с новым плагином Kotlin.

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

    В настоящее время такая загрузка ограничена build.gradle.kts и settings.gradle.kts файлами (пожалуйста, проголосуйте за соответствующую задачу). Чтобы включить выделение для init.gradle.kts или примененных скриптовых плагинов, используйте старый механизм — добавление их в автономные скрипты. Конфигурация для этих скриптов будет загружаться отдельно по мере необходимости. Вы также можете включить автоматическую перезагрузку для таких скриптов.

    *.gradle.kts – Add to standalone scripts
  • Более подробные сообщения об ошибках. Ранее вы могли видеть ошибки Gradle Daemon только в отдельных файлах журнала. Теперь Gradle Daemon возвращает всю информацию об ошибках непосредственно и отображает её в окне инструментов сборки. Это экономит ваше время и усилия.

Стандартная библиотека

Вот список наиболее значительных изменений в стандартной библиотеке Kotlin в версии 1.4.0:

  • Общий API обработки исключений

  • Новые функции для массивов и коллекций

  • Функции для обработки строк

  • Битовые операции

  • Улучшения делегированных свойств

  • Преобразование из KType в Java Type

  • Конфигурации ProGuard для рефлексии Kotlin

  • Улучшение существующего API

  • Описатели module-info для артефактов stdlib

  • Устаревшие функции

  • Исключение устаревшего экспериментального сопроцессора

Общий API обработки исключений

Следующие элементы API были перемещены в общую библиотеку:

  • Throwable.stackTraceToString() расширяющее свойство, которое возвращает подробное описание этого исключения со стеком вызовов, и Throwable.printStackTrace(), которое выводит это описание в стандартный поток ошибок.

  • Throwable.addSuppressed() функция, которая позволяет указать исключения, которые были подавлены для передачи исключения, и свойство Throwable.suppressedExceptions, которое возвращает список всех подавленных исключений.

  • @Throws аннотация, которая перечисляет типы исключений, которые будут проверены при компиляции функции в метод платформы (на JVM или нативных платформах).

Новые функции для массивов и коллекций

Коллекции

В версии 1.4.0 стандартная библиотека включает ряд полезных функций для работы с коллекциями:

  • setOfNotNull(), которая создает множество, состоящее из всех непустых элементов среди предоставленных аргументов.

    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 — реализацию двухсторонней очереди. Двухсторонняя очередь позволяет добавлять или удалять элементы как в начале, так и в конце очереди за амортизированное постоянное время. По умолчанию можно использовать двухстороннюю очередь, когда в коде требуется очередь или стек.

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
}
END_OF_DOCUMENT_MARKER

Улучшения делегированных свойств

В версии 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 Reflection

Начиная с версии 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-runtime, содержащие только модули платформы, необходимые для вашего приложения. Вы уже могли использовать jlink с артефактами стандартной библиотеки Kotlin, но для этого вам нужно было использовать отдельные артефакты (те, что с классификатором «модульный»), и вся настройка не была очевидной.
В Android убедитесь, что вы используете плагин Android Gradle версии 3.2 или выше, который может правильно обрабатывать файлы jar с module-info.

Устаревшие функции

toShort() и toByte() для Double и Float

Мы устарели функции toShort() и toByte() для Double и Float, потому что они могут приводить к неожиданным результатам из-за узкого диапазона значений и меньшего размера переменной.

Чтобы преобразовать числа с плавающей точкой в Byte или Short, используйте двухэтапное преобразование: сначала преобразуйте их в Int, а затем снова преобразуйте в целевой тип.

contains(), indexOf() и lastIndexOf() для массивов чисел с плавающей точкой

Мы устарели функции contains(), indexOf(), и lastIndexOf() для FloatArray и DoubleArray, потому что они используют стандартную обработку равенства IEEE 754, что противоречит равенству в общем порядке в некоторых крайних случаях. См. это сообщение об ошибке для получения подробностей.

Функции min() и max() для коллекций

Мы устарели функции min() и max() для коллекций в пользу minOrNull() и maxOrNull(), которые более точно отражают их поведение — возвращая null для пустых коллекций. См. это сообщение об ошибке для получения подробностей.

Исключение устаревших экспериментальных сопроцедур

API kotlin.coroutines.experimental был устаревшим в пользу kotlin.coroutines в версии 1.3.0. В 1.4.0 мы завершаем процесс устаревания kotlin.coroutines.experimental путем удаления его из стандартной библиотеки. Для тех, кто все еще использует его на JVM, мы предоставили артефакт совместимости kotlin-coroutines-experimental-compat.jar со всеми экспериментальными API сопроцедур. Мы опубликовали его в Maven, и он включен в дистрибутив Kotlin вместе со стандартной библиотекой.

Стабильная сериализация JSON

С Kotlin 1.4.0 мы поставляем первую стабильную версию kotlinx.serialization - 1.0.0-RC. Теперь мы рады объявить API сериализации JSON в kotlinx-serialization-core (ранее известный как kotlinx-serialization-runtime) стабильным. Библиотеки для других форматов сериализации остаются экспериментальными, наряду с некоторыми продвинутыми частями основной библиотеки.

Мы существенно переработали API сериализации JSON, чтобы сделать его более последовательным и удобным в использовании. Отныне мы будем продолжать развивать API сериализации JSON в обратной совместимости. Однако, если вы использовали предыдущие версии, вам нужно будет переписать часть кода при переходе на версию 1.0.0-RC. Чтобы помочь вам в этом, мы также предлагаем Руководство по сериализации Kotlin — полный набор документации для kotlinx.serialization. Оно проведет вас через процесс использования самых важных функций и поможет решить возможные проблемы.

Примечание: kotlinx-serialization 1.0.0-RC работает только с компилятором Kotlin 1.4. Более ранние версии компилятора несовместимы.

END_OF_DOCUMENT_MARKER

Скрипты и REPL

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

  • Новый API разрешения зависимостей

  • Новый API REPL

  • Кэш скомпилированных скриптов

  • Переименование артефактов

Чтобы помочь вам лучше освоить скрипты на Kotlin, мы подготовили проект с примерами. Он содержит примеры стандартных скриптов (*.main.kts) и примеры использования API Kotlin Scripting и пользовательских определений скриптов. Пожалуйста, попробуйте и поделитесь своим отзывом, используя наш отслеживатель задач.

Новый API разрешения зависимостей

В версии 1.4.0 мы представили новый API для разрешения внешних зависимостей (таких как артефакты Maven), вместе с его реализациями. Этот API опубликован в новых артефактах kotlin-scripting-dependencies и kotlin-scripting-dependencies-maven. Предыдущая функциональность разрешения зависимостей в библиотеке kotlin-script-util теперь устарела.

Новый API REPL

Новый экспериментальный API REPL теперь является частью API Kotlin Scripting. Также существует несколько его реализаций в опубликованных артефактах, некоторые из которых обладают расширенной функциональностью, такой как автодополнение кода. Мы используем этот API в ядре Kotlin Jupyter, и теперь вы можете использовать его в собственных оболочках и REPL.

Кэш скомпилированных скриптов

API Kotlin Scripting теперь предоставляет возможность реализовать кэш скомпилированных скриптов, что значительно ускоряет последующее выполнение неизменённых скриптов. Наша стандартная расширенная реализация скриптов kotlin-main-kts уже имеет собственный кэш.

Переименование артефактов

Для избежания путаницы с именами артефактов, мы переименовали kotlin-scripting-jsr223-embeddable и kotlin-scripting-jvm-host-embeddable просто в kotlin-scripting-jsr223 и kotlin-scripting-jvm-host. Эти артефакты зависят от артефакта kotlin-compiler-embeddable, который затемняет связанные сторонние библиотеки, чтобы избежать конфликтов использования. С этим переименованием мы делаем использование kotlin-compiler-embeddable (что в целом безопаснее) стандартным для скриптовых артефактов. Если по какой-либо причине вам нужны артефакты, зависящие от незатемнённого kotlin-compiler, используйте версии артефактов с суффиксом -unshaded, например, kotlin-scripting-jsr223-unshaded. Обратите внимание, что это переименование затрагивает только скриптовые артефакты, которые предполагается использовать напрямую; имена других артефактов остаются неизменными.

Миграция на Kotlin 1.4.0

Инструменты миграции плагина Kotlin помогут вам переместить ваши проекты из более ранних версий Kotlin в 1.4.0.

Просто измените версию Kotlin на 1.4.0 и повторно импортируйте свой проект Gradle или Maven. IDE запросит вас о миграции.

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

Run migration

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

Migration inspections

Kotlin 1.4.0 является выпуском новой функции и поэтому может вносить несовместимые изменения в язык. Подробный список таких изменений можно найти в Руководстве по совместимости для Kotlin 1.4.

Последнее изменение: 07 апреля 2022
Что нового в Kotlin 1.4.20 Что нового в Kotlin 1.3

© 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

Spec-Zone.ru

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