Что нового в Kotlin 1.4.0
В Kotlin 1.4.0 мы реализовали ряд улучшений во всех его компонентах, с фокусом на качество и производительность. Ниже вы найдете список наиболее важных изменений в Kotlin 1.4.0.
- Преобразования SAM для Kotlin-интерфейсов
- Явный режим API для авторов библиотек
- Смешивание именованных и позиционных аргументов
- Заключительная запятая
- Улучшения вызываемых ссылок
breakиcontinueвнутриwhen, включенные в циклы
- Новый бэкэнд JVM IR
- Новые режимы генерации методов по умолчанию в интерфейсах
- Единый тип исключений для проверок на null
- Аннотации типов в байткоде JVM
- Поддержка приостанавливаемых функций Kotlin в Swift и Objective-C
- Поддержка Objective-C дженериков по умолчанию
- Обработка исключений в Objective-C/Swift взаимодействии
- Генерация релизных
.dSYMна целевых платформах Apple по умолчанию - Улучшения производительности
- Упрощенное управление зависимостями CocoaPods
- Использование кода на нескольких платформах с иерархической структурой проекта
- Использование нативных библиотек в иерархической структуре
- Указание зависимостей kotlinx только один раз
- Зависимость от стандартной библиотеки теперь добавляется по умолчанию
- Проекты Kotlin требуют недавнюю версию Gradle
- Улучшенная поддержка Kotlin Gradle DSL в IDE
- Общий API обработки исключений
- Новые функции для массивов и коллекций
- Функции для обработки строк
- Битовые операции
- Улучшения делегированных свойств
- Преобразование из KType в Java Type
- Настройки Proguard для рефлексии Kotlin
- Улучшение существующего API
- Описание module-info для артефактов stdlib
- Устаревшие элементы
- Исключение устаревших экспериментальных корутин
- Новый API разрешения зависимостей
- Новый API REPL
- Кэш скомпилированных скриптов
- Переименование артефактов
Особенности языка и улучшения
Kotlin 1.4.0 поставляется с различными функциями языка и улучшениями. Они включают:
- Преобразования SAM для Kotlin-интерфейсов
- Смешивание именованных и позиционных аргументов
- Заключительная запятая
- Улучшения вызываемых ссылок
breakиcontinueвнутриwhen, включенные в циклы- Явный режим API для авторов библиотек
Преобразования SAM для Kotlin-интерфейсов
До Kotlin 1.4.0 преобразования SAM (Single Abstract Method) применялись только при работе с Java-методами и Java-интерфейсами из Kotlin. Теперь их можно использовать и для 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 могут генерировать ошибки (строгий режим) или предупреждения (режим предупреждений). Некоторые виды объявлений исключаются из таких проверок ради удобочитаемости и здравого смысла:
END_OF_DOCUMENT_MARKER- Основные конструкторы
- Свойства классов данных
- Геттеры и сеттеры свойств
-
overrideметоды
Режим явного API анализирует только производственные источники модуля.
Чтобы скомпилировать свой модуль в режиме явного API, добавьте следующие строки в свой скрипт Gradle:
kotlin {
// for strict mode
explicitApi()
// or
explicitApi = 'strict'
// for warning mode
explicitApiWarning()
// or
explicitApi = 'warning'
}
kotlin {
// for strict mode
explicitApi()
// or
explicitApi = ExplicitApiMode.Strict
// for warning mode
explicitApiWarning()
// or
explicitApi = ExplicitApiMode.Warning
}
При использовании компилятора командной строки переключитесь на режим явного API, добавив опцию компилятора -Xexplicit-api со значением strict или warning.
-Xexplicit-api={strict|warning}
Дополнительные сведения о режиме явного API см. в KEEP.
Смешивание именованных и позиционных аргументов
В Kotlin 1.3, когда вы вызывали функцию с именованными аргументами, вам нужно было поместить все аргументы без имён (позиционные аргументы) перед первым именованным аргументом. Например, вы могли вызвать f(1, y = 2), но не могли вызвать f(x = 1, 2).
Это было действительно раздражающим, когда все аргументы были в правильных позициях, но вы хотели указать имя для одного аргумента посередине. Это было особенно полезно для того, чтобы совершенно ясно указать, к какому атрибуту относится значение boolean или null.
В Kotlin 1.4 такого ограничения нет – теперь вы можете указать имя для аргумента посередине набора позиционных аргументов. Более того, вы можете смешивать позиционные и именованные аргументы любым способом, если они остаются в правильном порядке.
fun reformat(
str: String,
uppercaseFirstLetter: Boolean = true,
wordSeparator: Char = ' '
) {
// ...
}
//Function call with a named argument in the middle
reformat("This is a String!", uppercaseFirstLetter = false , '-')
Конечная запятая
С Kotlin 1.4 вы можете добавлять конечную запятую в перечислениях, таких как списки аргументов и параметров, when записях и компонентах деструктурирующих объявлений. С конечной запятой вы можете добавлять новые элементы и изменять их порядок, не добавляя и не удаляя запятые.
Это особенно полезно, если вы используете многострочный синтаксис для параметров или значений. После добавления конечной запятой вы можете легко поменять строки с параметрами или значениями.
fun reformat(
str: String,
uppercaseFirstLetter: Boolean = true,
wordSeparator: Character = ' ', //trailing comma
) {
// ...
}
val colors = listOf(
"red",
"green",
"blue", //trailing comma
)
Улучшения вызываемых ссылок
Kotlin 1.4 поддерживает больше случаев использования вызываемых ссылок:
- Ссылки на функции с значениями по умолчанию
- Ссылки на функции в функциях, возвращающих
Unit - Ссылки, которые адаптируются в зависимости от количества аргументов в функции
- Преобразование suspend для вызываемых ссылок
Ссылки на функции с значениями по умолчанию
Теперь вы можете использовать вызываемые ссылки на функции с значениями по умолчанию. Если вызываемая ссылка на функцию foo не принимает аргументов, используется значение по умолчанию 0.
fun foo(i: Int = 0): String = "$i!"
fun apply(func: () -> String): String = func()
fun main() {
println(apply(::foo))
}
Ранее вам нужно было писать дополнительные перегрузки для функции apply для использования значений по умолчанию.
// some new overload fun applyInt(func: (Int) -> String): String = func(0)
Ссылки на функции в функциях, возвращающих Unit
В Kotlin 1.4 вы можете использовать вызываемые ссылки на функции, возвращающие любой тип, в функциях, возвращающих Unit. До Kotlin 1.4 в этом случае вы могли использовать только лямбда-аргументы. Теперь вы можете использовать как лямбда-аргументы, так и вызываемые ссылки.
fun foo(f: () -> Unit) { }
fun returnsInt(): Int = 42
fun main() {
foo { returnsInt() } // this was the only way to do it before 1.4
foo(::returnsInt) // starting from 1.4, this also works
}
Ссылки, которые адаптируются в зависимости от количества аргументов в функции
Теперь вы можете адаптировать вызываемые ссылки на функции при передаче переменного количества аргументов (vararg) . Вы можете передавать любое количество параметров одного типа в конце списка переданных аргументов.
fun foo(x: Int, vararg y: String) {}
fun use0(f: (Int) -> Unit) {}
fun use1(f: (Int, String) -> Unit) {}
fun use2(f: (Int, String, String) -> Unit) {}
fun test() {
use0(::foo)
use1(::foo)
use2(::foo)
}
Преобразование suspend для вызываемых ссылок
В дополнение к преобразованию suspend для лямбд, Kotlin теперь поддерживает преобразование suspend для вызываемых ссылок, начиная с версии 1.4.0.
fun call() {}
fun takeSuspend(f: suspend () -> Unit) {}
fun test() {
takeSuspend { call() } // OK before 1.4
takeSuspend(::call) // In Kotlin 1.4, it also works
}
Использование break и continue внутри выражений when в циклах
В Kotlin 1.3 вы не могли использовать неквалифицированные break и continue внутри выражений when в циклах. Причина заключалась в том, что эти ключевые слова были зарезервированы для возможного поведения прохода в выражениях when.
Поэтому, если вы хотели использовать break и continue внутри выражений when в циклах, вам нужно было пометить их, что стало довольно громоздким.
fun test(xs: List<Int>) {
LOOP@for (x in xs) {
when (x) {
2 -> continue@LOOP
17 -> break@LOOP
else -> println(x)
}
}
}
В Kotlin 1.4 вы можете использовать break и continue без меток внутри выражений when в циклах. Они ведут себя ожидаемым образом, завершая ближайший вложенный цикл или переходя к его следующей итерации.
fun test(xs: List<Int>) {
for (x in xs) {
when (x) {
2 -> continue
17 -> break
else -> println(x)
}
}
}
Поведение прохода внутри выражений when будет дальше прорабатываться.
Новые инструменты в IDE
С Kotlin 1.4 вы можете использовать новые инструменты в IntelliJ IDEA для упрощения разработки на Kotlin:
Новый гибкий мастер проектов
С гибким новым мастером проектов Kotlin у вас есть место для простого создания и настройки различных типов проектов Kotlin, включая проекты многоплатформенной разработки, которые могут быть сложными для настройки без графического интерфейса.
Новый мастер проектов Kotlin прост и гибок:
- Выберите шаблон проекта в зависимости от того, что вы хотите сделать. В будущем будет добавлено больше шаблонов.
-
Выберите систему сборки – Gradle (Kotlin или Groovy DSL), Maven или IntelliJ IDEA.
Мастер проектов Kotlin будет отображать только поддерживаемые системы сборки для выбранного шаблона проекта. - Предварительно просмотрите структуру проекта прямо на главном экране.
Затем вы можете завершить создание проекта или, по желанию, настроить проект на следующем экране:
- Добавить/удалить модули и целевые платформы, поддерживаемые для этого шаблона проекта.
- Настроить параметры модуля и целевой платформы, например, целевую версию JVM, шаблон целевой платформы и фреймворк для тестов.
В будущем мы собираемся сделать мастер проектов Kotlin еще более гибким, добавив больше параметров настройки и шаблонов.
Вы можете опробовать новый мастер проектов Kotlin, пройдя эти учебники:
- Создать консольное приложение на основе Kotlin/JVM
- Создать приложение Kotlin/JS для React
- Создать приложение Kotlin/Native
Отладчик корутин
Многие уже используют корутины для асинхронной разработки. Но когда дело доходило до отладки, работа с корутинами до Kotlin 1.4 могла быть очень сложной. Поскольку корутины переключались между потоками, было трудно понять, что делает конкретная корутина, и проверить её контекст. В некоторых случаях отслеживание шагов по точкам останова просто не работало. В результате вам приходилось полагаться на логирование или умственные усилия для отладки кода, использующего корутины.
В Kotlin 1.4 отладка корутин стала намного удобнее благодаря новым функциям, поставляемым с плагином Kotlin.
Отладка работает для версий 1.3.8 или выше
kotlinx-coroutines-core.
Окно Инструментов отладки теперь содержит новую вкладку Корутины. На этой вкладке вы можете найти информацию как о выполняющихся, так и о приостановленных корутинах. Корутины сгруппированы по диспетчерам, на которых они выполняются.

Теперь вы можете:
- Легко проверять состояние каждой корутины.
- Просматривать значения локальных и захваченных переменных для выполняющихся и приостановленных корутин.
- Просматривать полный стек создания корутин, а также стек вызовов внутри корутины. Стек включает все фреймы с значениями переменных, даже те, которые теряются во время стандартной отладки.
Если вам нужен полный отчет, содержащий состояние каждой корутины и её стек, щелкните правой кнопкой мыши внутри вкладки Корутины, а затем нажмите Получить дамп корутин. В настоящее время дамп корутин довольно прост, но в будущих версиях Kotlin мы сделаем его более читаемым и полезным.
Узнайте больше о отладке корутин в этой статье блога и документации 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, которые в настоящее время находятся в стадии альфа-версии, и поделиться с нами своими отзывами.
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 default. Для этой цели был механизм, который включает аннотацию @JvmDefault для маркировки таких методов и опцию компилятора -Xjvm-default, которая включает обработку этой аннотации.
В версии 1.4.0 мы добавили новый режим генерации методов по умолчанию: -Xjvm-default=all компилирует все неабстрактные методы интерфейсов Kotlin в методы Java default. Для совместимости с кодом, использующим интерфейсы, скомпилированные без default, мы также добавили режим all-compatibility.
Для получения дополнительной информации о методах по умолчанию в Java-взаимодействии, см. документацию документацию и эту статью блога.
Единый тип исключения для проверок на null
Начиная с Kotlin 1.4.0, все проверки на null во время выполнения будут выбрасывать исключение типа java.lang.NullPointerException вместо KotlinNullPointerException, IllegalStateException, IllegalArgumentException, и TypeCastException. Это относится к: оператору !!, проверкам null параметров в преамбуле метода, проверкам null платформно-типизированных выражений и оператору as с типом non-null. Это не относится к lateinit проверкам null и явным вызовам функций библиотеки, таким как checkNotNull или requireNotNull.
Это изменение увеличивает количество возможных оптимизаций проверок на null, которые могут быть выполнены либо компилятором Kotlin, либо различными инструментами обработки байткода, такими как оптимизатор Android R8.
Обратите внимание, что с точки зрения разработчика, изменения не будут существенными: код Kotlin будет генерировать исключения с теми же сообщениями об ошибках, что и раньше. Тип исключения меняется, но информация, передаваемая в нём, остаётся неизменной.
Аннотации типов в байткоде JVM
Kotlin теперь может генерировать аннотации типов в байткоде JVM (версия целевой платформы 1.8+), чтобы они были доступны в Java-рефлексии во время выполнения. Чтобы вывести аннотацию типа в байткоде, выполните следующие действия:
- Убедитесь, что ваша объявленная аннотация имеет правильную метку назначения (Java
ElementType.TYPE_USEили KotlinAnnotationTarget.TYPE) и сохранение (AnnotationRetention.RUNTIME). - Скомпилируйте объявление класса аннотации в байткод JVM с целевой версией 1.8+. Вы можете указать её с помощью опции компилятора
-jvm-target=1.8. - Скомпилируйте код, использующий аннотацию, в байткод JVM с целевой версией 1.8+ (
-jvm-target=1.8), и добавьте опцию компилятора-Xemit-jvm-type-annotations.
Обратите внимание, что аннотации типов из стандартной библиотеки пока не выводятся в байткод, так как стандартная библиотека скомпилирована с целевой версией 1.6.
В настоящее время поддерживаются только базовые случаи:
- Аннотации типов на параметрах методов, типах возвращаемых значений методов и типах свойств;
- Инвариантные проекции аргументов типов, такие как
Smth<@Ann Foo>,Array<@Ann Foo>.
В следующем примере аннотация @Foo на типе String может быть выведена в байткод и затем использоваться кодом библиотеки:
@Target(AnnotationTarget.TYPE)
annotation class Foo
class A {
fun foo(): @Foo String = "OK"
}
Kotlin/JS
На платформе JS Kotlin 1.4.0 предоставляет следующие улучшения:
Новая Gradle DSL
Плагин Gradle kotlin.js поставляется с изменённой Gradle DSL, которая предоставляет ряд новых параметров конфигурации и более тесно интегрирована с DSL, используемой плагином kotlin-multiplatform. Некоторые из самых значимых изменений включают:
- Явные переключатели для создания исполняемых файлов с помощью
binaries.executable(). Подробнее об исполнении Kotlin/JS и его среде читайте здесь. - Конфигурация загрузчиков CSS и стилей webpack из конфигурации Gradle с помощью
cssSupport. Подробнее об их использовании читайте здесь. - Улучшенное управление зависимостями npm с обязательными номерами версий или диапазонами версий semver, а также поддержка зависимостей npm development, 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
- Генерация релизных
.dSYMпо умолчанию на целевых платформах 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/Swift
В версии 1.4.0 мы немного изменили Swift API, сгенерированный из Kotlin, в отношении обработки исключений. Существует фундаментальное различие в обработке ошибок между Kotlin и Swift. Все исключения Kotlin являются необрабатываемыми, в то время как Swift имеет только проверяемые ошибки. Таким образом, чтобы Swift-код знал об ожидаемых исключениях, функции Kotlin должны быть помечены аннотацией @Throws, указывающей список потенциальных классов исключений.
При компиляции в Swift или Objective-C фреймворк, функции, которые имеют или наследуют аннотацию @Throws, представлены как методы, генерирующие NSError* в Objective-C и как throws методы в Swift.
Ранее любые исключения, кроме RuntimeException и Error, распространялись как NSError. Теперь это поведение меняется: теперь NSError выбрасывается только для исключений, которые являются экземплярами классов, указанных в качестве параметров аннотации @Throws (или их подклассов). Другие исключения Kotlin, которые достигают Swift/Objective-C, считаются необработанными и вызывают завершение программы.
Генерация файлов отладки .dSYM по умолчанию для 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-interop. С новым инструментом Kotlin/Native создает библиотеки interop до 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
Кроссплатформенные проекты находятся в стадии Alpha. Функции языка и инструменты могут измениться в будущих версиях Kotlin.
Kotlin Multiplatform сокращает время, затрачиваемое на написание и поддержку одного и того же кода для разных платформ, сохраняя гибкость и преимущества нативного программирования. Мы продолжаем инвестировать в функции и улучшения многоплатформенной поддержки:
- Общий код для нескольких мишеней с помощью иерархической структуры проекта
- Использование нативных библиотек в иерархической структуре
- Указывать зависимости kotlinx только один раз
Для кроссплатформенных проектов требуется Gradle 6.0 или более поздняя версия.
Общий код для нескольких мишеней с помощью иерархической структуры проекта
С поддержкой новой иерархической структуры проекта вы можете использовать общий код для нескольких платформ в кроссплатформенном проекте.
Ранее любой код, добавленный в кроссплатформенный проект, мог располагаться либо в наборе исходников, специфичных для платформы, что ограничивает использование одной мишенью и не позволяет повторно использовать его другими платформами, либо в общем наборе исходников, например, commonMain или commonTest, который используется на всех платформах проекта. В общем наборе исходников вы могли вызывать платформенно-специфический API только с помощью expect объявления, требующего платформенно-специфических actual реализаций.
Это упрощало использование кода на всех платформах, но не упрощало использование кода только между некоторыми мишенями, особенно похожими мишенями, которые потенциально могли бы повторно использовать много общего логики и сторонних API.
Например, в типичном многоплатформенном проекте, нацеленном на iOS, есть две связанные с iOS мишени: одна для устройств iOS ARM64, а другая для симулятора x64. У них есть отдельные наборы исходников, специфичные для платформы, но на практике редко требуется разный код для устройства и симулятора, и их зависимости очень похожи. Таким образом, iOS-специфичный код можно использовать совместно.
Очевидно, в такой настройке желательно иметь общий набор исходников для двух мишеней iOS с Kotlin/Native-кодом, который все еще может напрямую вызывать любые API, общие как для iOS-устройств, так и для симулятора.
Теперь это можно сделать с помощью поддержки иерархической структуры проекта, которая выводит и адаптирует доступные API и функции языка в каждом наборе исходников на основе того, какие мишени их используют.
Для распространённых комбинаций мишеней вы можете создать иерархическую структуру с ярлыками мишеней.
Например, создайте две мишени iOS и общий набор исходников, показанный выше, с ярлыком ios():
kotlin {
ios() // iOS device and simulator targets; iosMain and iosTest source sets
}
Для других комбинаций мишеней создайте иерархию вручную, соединив наборы исходников с отношением dependsOn.
kotlin {
sourceSets {
desktopMain {
dependsOn(commonMain)
}
linuxX64Main {
dependsOn(desktopMain)
}
mingwX64Main {
dependsOn(desktopMain)
}
macosX64Main {
dependsOn(desktopMain)
}
}
}
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)
}
}
}
Благодаря иерархической структуре проекта библиотеки также могут предоставлять общие API для подмножества мишеней. Подробнее о общности кода в библиотеках.
Использование нативных библиотек в иерархической структуре
Вы можете использовать платформенно-зависимые библиотеки, такие как Foundation, UIKit, и posix, в наборах исходников, используемых несколькими нативными мишенями. Это поможет вам использовать больше нативного кода, не ограничиваясь зависимостями, специфичными для платформы.
Дополнительные шаги не требуются – всё выполняется автоматически. IntelliJ IDEA поможет вам определить общие объявления, которые можно использовать в общем коде.
Узнайте больше о использовании платформенно-зависимых библиотек.
Указание зависимостей только один раз
Отныне вместо указания зависимостей от различных вариантов одной и той же библиотеки в общих и платформенно-специфических наборах исходных кодов, где она используется, вы должны указать зависимость только один раз в общем наборе исходных кодов.
kotlin {
sourceSets {
commonMain {
dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1'
}
}
}
}
kotlin {
sourceSets {
val commonMain by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1")
}
}
}
}
Не используйте имена артефактов библиотек 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:
- Зависимость от стандартной библиотеки теперь добавляется по умолчанию
- Проекты 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 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 обработки исключений
- Новые функции для массивов и коллекций
- Функции для манипуляций со строками
- Битовые операции
- Улучшения делегированных свойств
- Преобразование из 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()для соблюдения соглашения об именовании, используемого в Kotlin-коллекциях API. Суффикс*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()добавлены в общую библиотеку. Они заменяют JVM-специфичные функцииappendln()этих классов.fun main() { //sampleStart println(buildString { appendLine("Hello,") appendLine("world") }) //sampleEnd }
Битовые операции
Новые функции для битовых операций:
countOneBits()countLeadingZeroBits()countTrailingZeroBits()takeHighestOneBit()takeLowestOneBit()-
rotateLeft()иrotateRight()(экспериментальные)
fun main() {
//sampleStart
val number = "1010000".toInt(radix = 2)
println(number.countOneBits())
println(number.countTrailingZeroBits())
println(number.takeHighestOneBit().toString(2))
//sampleEnd
}
Улучшения делегированных свойств
В версии 1.4.0 мы добавили новые возможности для улучшения работы с делегированными свойствами в Kotlin:
- Теперь свойство может быть делегировано другому свойству.
- Новый интерфейс
PropertyDelegateProviderпомогает создавать поставщиков делегатов в одном объявлении. -
ReadWritePropertyтеперь расширяетReadOnlyProperty, поэтому вы можете использовать оба для свойств только для чтения.
Помимо нового API, мы внесли некоторые оптимизации, уменьшающие размер итогового байт-кода. Эти оптимизации описаны в этой статье блога.
Для получения дополнительной информации о делегированных свойствах, см. документацию.
Преобразование из KType в Java Type
Новое расширяющее свойство KType.javaType (в настоящее время экспериментальное) в stdlib помогает получить 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, содержащие только те платформенные модули, которые необходимы для вашего приложения. Вы уже могли использовать jlink с артефактами стандартной библиотеки Kotlin, но для этого нужно было использовать отдельные артефакты — те, у которых был классификатор «modular» — и вся настройка не была простой.
В Android убедитесь, что вы используете плагин Android Gradle версии 3.2 или выше, который может корректно обрабатывать jar-файлы с module-info.
Устаревшие функции
toShort() и toByte() для Double и Float
Мы устарели функции toShort() и toByte() для Double и Float, так как они могли приводить к неожиданным результатам из-за узкого диапазона значений и меньшего размера переменных.
Для преобразования чисел с плавающей запятой в Byte или Short, используйте двухэтапное преобразование: сначала преобразуйте их в Int, а затем снова в целевой тип.
contains(), indexOf() и lastIndexOf() для массивов чисел с плавающей запятой
Мы устарели функции contains(), indexOf() и lastIndexOf() для FloatArray и DoubleArray, так как они используют стандартное равенство IEEE 754, которое в некоторых граничных случаях противоречит равенству с полным порядком. Подробности см. в этой задаче.
Функции min() и max() для коллекций
Мы устарели функции min() и max() для коллекций в пользу minOrNull() и maxOrNull(), которые более точно отражают их поведение — возвращая null для пустых коллекций. Подробности см. в этой задаче.
Исключение устаревших экспериментальных сопроцедур
API kotlin.coroutines.experimental устарел в пользу kotlin.coroutines в 1.3.0. В 1.4.0 мы завершаем цикл устаревания kotlin.coroutines.experimental, удалив его из стандартной библиотеки. Для тех, кто все еще использует его на JVM, мы предоставили артефакт совместимости kotlin-coroutines-experimental-compat.jar со всеми экспериментальными API сопроцедур. Мы опубликовали его в Maven, и он включен в дистрибутив Kotlin наряду со стандартной библиотекой.
Стабильная сериализация JSON
В Kotlin 1.4.0 мы выпустили первую стабильную версию kotlinx.serialization - 1.0.0-RC. Теперь мы рады объявить API сериализации JSON в kotlinx-serialization-core (ранее известный как kotlinx-serialization-runtime) стабильным. Библиотеки для других форматов сериализации остаются экспериментальными, наряду с некоторыми расширенными частями основной библиотеки.
Мы значительно переработали API сериализации JSON, чтобы сделать его более согласованным и удобным в использовании. В дальнейшем мы будем продолжать развивать API сериализации JSON в обратной совместимости. Однако, если вы использовали предыдущие версии, вам потребуется переписать часть своего кода при переходе на 1.0.0-RC. Для помощи в этом мы также предлагаем Руководство по сериализации Kotlin — полный набор документации для kotlinx.serialization. Он проведет вас через процесс использования самых важных функций и поможет решить любые возникающие проблемы.
Примечание:
kotlinx-serialization1.0.0-RC работает только с компилятором Kotlin 1.4. Более ранние версии компилятора несовместимы.
Скриптинг и REPL
В 1.4.0 скриптинг на Kotlin получил ряд функциональных и производительных улучшений, а также другие обновления. Вот некоторые ключевые изменения:
- Новый API разрешения зависимостей
- Новый API REPL
- Кэш скомпилированных скриптов
- Переименование артефактов
Для того, чтобы помочь вам лучше ознакомиться со скриптингом на 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 в Kotlin Jupyter kernel, и теперь вы можете попробовать его в собственных пользовательских оболочках и 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.
Инспекции кода имеют разные уровни серьезности, чтобы помочь вам решить, какие предложения принять, а какие игнорировать.
Миграция проектов multiplatform
Чтобы помочь вам начать использовать новые функции Kotlin multiplatform в существующих проектах, мы публикуем руководство по миграции для проектов multiplatform.
© 2010–2020 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/reference/whatsnew14.html