Spec-Zone.ru › Kotlin 2

Что нового в Kotlin 2.1.0

Выпуск: 27 ноября 2024 г.

Вышел Kotlin 2.1.0! Основные нововведения:

  • Новые возможности языка в предварительной версии: Условия-ограничители в when с subject, нелокальные break и continue, а также интерполяция строк с несколькими знаками доллара.

  • Обновления компилятора K2: Больше гибкости при выполнении проверок компилятора и улучшения реализации kapt.

  • Kotlin Multiplatform: добавлена базовая поддержка экспорта в Swift, стабильный DSL Gradle для параметров компилятора и многое другое.

  • Kotlin/Native: Улучшена поддержка iosArm64 и добавлены другие обновления.

  • Kotlin/Wasm: множество обновлений, включая поддержку инкрементальной компиляции.

  • Поддержка Gradle: Улучшена совместимость с новыми версиями Gradle и плагина Android Gradle, а также обновлен API плагина Kotlin Gradle.

  • Документация: Значительно улучшена документация Kotlin.

Информацию о цикле выпуска Kotlin см. в разделе Процесс выпуска Kotlin.

Поддержка IDE

Плагины Kotlin с поддержкой версии 2.1.0 входят в состав последних версий IntelliJ IDEA и Android Studio. Обновлять плагин Kotlin в IDE не нужно. Достаточно изменить версию Kotlin на 2.1.0 в сценариях сборки.

Подробнее см. в разделе Обновление до новой версии Kotlin.

Язык

После выпуска Kotlin 2.0.0 с компилятором K2 команда JetBrains сосредоточилась на развитии языка и добавлении новых возможностей. В этом выпуске мы рады представить несколько новых улучшений в дизайне языка.

Эти возможности доступны в предварительной версии. Мы предлагаем вам попробовать их и поделиться отзывами:

  • Условия-ограничители в when с subject

  • Нелокальные break и continue

  • Интерполяция с несколькими знаками доллара: улучшенная обработка $ в строковых литералах

Все эти возможности поддерживаются в IDE в последней версии IntelliJ IDEA 2024.3 при включенном режиме K2.

Подробнее см. в публикации в блоге об IntelliJ IDEA 2024.3.

Полный список возможностей и предложений по развитию языка Kotlin.

В этот выпуск также вошли следующие обновления языка:

  • Поддержка обязательного opt-in для расширения API

  • Улучшено разрешение перегрузок функций с обобщенными типами

  • Улучшены проверки полноты выражений when с sealed-классами

Условия-ограничители в when с subject

Эта возможность находится в предварительной версии; для ее использования требуется opt-in (подробности ниже).

Будем благодарны за ваши отзывы в YouTrack.

Начиная с версии 2.1.0, в выражениях и инструкциях when с subject можно использовать условия-ограничители.

Условия-ограничители позволяют задать для ветвей выражения when несколько условий, благодаря чему сложные потоки управления становятся более явными и лаконичными, а структура кода — проще.

Чтобы добавить условие-ограничитель в ветвь, поместите его после основного условия, отделив с помощью if:

sealed interface Animal {
    data class Cat(val mouseHunter: Boolean) : Animal {
        fun feedCat() {}
    }

    data class Dog(val breed: String) : Animal {
        fun feedDog() {}
    }
}

fun feedAnimal(animal: Animal) {
    when (animal) {
        // Branch with only the primary condition. Calls `feedDog()` when `animal` is `Dog`
        is Animal.Dog -> animal.feedDog()
        // Branch with both primary and guard conditions. Calls `feedCat()` when `animal` is `Cat` and is not `mouseHunter`
        is Animal.Cat if !animal.mouseHunter -> animal.feedCat()
        // Prints "Unknown animal" if none of the above conditions match
        else -> println("Unknown animal")
    }
}

В одном выражении when можно сочетать ветви с условиями-ограничителями и без них. Код в ветви с условием-ограничителем выполняется, только если и основное условие, и условие-ограничитель имеют значение true. Если основное условие не совпадает, условие-ограничитель не вычисляется. Кроме того, условия-ограничители поддерживают else if.

Чтобы включить условия-ограничители в проекте, используйте следующий параметр компилятора в командной строке:

kotlinc -Xwhen-guards main.kt

Или добавьте его в блок compilerOptions {} файла сборки Gradle:

// build.gradle.kts
kotlin {
    compilerOptions {
        freeCompilerArgs.add("-Xwhen-guards")
    }
}

Нелокальные break и continue

Эта возможность находится в предварительной версии; для ее использования требуется opt-in (подробности ниже).

Будем благодарны за ваши отзывы в YouTrack.

В Kotlin 2.1.0 появилась предварительная версия еще одной долгожданной возможности — поддержки нелокальных break и continue. Эта возможность расширяет набор инструментов, доступных в области видимости inline-функций, и позволяет сократить шаблонный код в проекте.

Ранее можно было использовать только нелокальные возвраты. Теперь Kotlin также поддерживает нелокальные break и continue — выражения перехода. Это означает, что их можно применять внутри лямбд, переданных в качестве аргументов inline-функции, охватывающей цикл:

fun processList(elements: List<Int>): Boolean {
    for (element in elements) {
        val variable = element.nullableMethod() ?: run {
            log.warning("Element is null or invalid, continuing...")
            continue
        }
        if (variable == 0) return true // If variable is zero, return true
    }
    return false
}

Чтобы попробовать эту возможность в проекте, используйте параметр компилятора -Xnon-local-break-continue в командной строке:

kotlinc -Xnon-local-break-continue main.kt

Или добавьте его в блок compilerOptions {} файла сборки Gradle:

// build.gradle.kts
kotlin {
    compilerOptions {
        freeCompilerArgs.add("-Xnon-local-break-continue")
    }
}

Мы планируем сделать эту возможность стабильной в будущих выпусках Kotlin. Если при использовании нелокальных break и continue возникнут проблемы, сообщите о них в систему отслеживания ошибок.

Интерполяция строк с несколькими знаками доллара

Эта возможность находится в предварительной версии; для ее использования требуется opt-in (подробности ниже).

Будем благодарны за ваши отзывы в YouTrack.

В Kotlin 2.1.0 добавлена поддержка интерполяции строк с несколькими знаками доллара, что позволяет лучше обрабатывать знак доллара ($) в строковых литералах. Эта возможность полезна в ситуациях, когда требуется несколько знаков доллара, например в шаблонизаторах, схемах JSON и других форматах данных.

В Kotlin для интерполяции строк используется один знак доллара. Однако для вставки в строку буквального знака доллара, часто встречающегося в финансовых данных и системах шаблонов, приходилось использовать обходные решения, например ${'$'}. Если включить интерполяцию с несколькими знаками доллара, можно настроить, сколько знаков доллара запускают интерполяцию; меньшее количество знаков будет восприниматься как строковые литералы.

Ниже показан пример создания многострочной строки со схемой JSON и заполнителями с помощью $:

val KClass<*>.jsonSchema : String
    get() = $$"""
    {
      "$schema": "https://json-schema.org/draft/2020-12/schema",
      "$id": "https://example.com/product.schema.json",
      "$dynamicAnchor": "meta"
      "title": "$${simpleName ?: qualifiedName ?: "unknown"}",
      "type": "object"
    }
    """

В этом примере начальный $$ означает, что для запуска интерполяции нужны два знака доллара ($$). Это позволяет не интерпретировать $schema, $id и $dynamicAnchor как маркеры интерполяции.

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

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

kotlinc -Xmulti-dollar-interpolation main.kt

Или измените блок compilerOptions {} файла сборки Gradle:

// build.gradle.kts
kotlin {
    compilerOptions {
        freeCompilerArgs.add("-Xmulti-dollar-interpolation")
    }
}

Если в коде уже используется стандартная интерполяция строк с одним знаком доллара, ничего менять не нужно. Используйте $$, когда в строках нужны буквальные знаки доллара.

Поддержка обязательного opt-in для расширения API

В Kotlin 2.1.0 добавлена аннотация @SubclassOptInRequired, позволяющая авторам библиотек требовать явный opt-in перед тем, как пользователи смогут реализовывать экспериментальные интерфейсы или наследоваться от экспериментальных классов.

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

Чтобы добавить требование opt-in к элементу API, используйте аннотацию @SubclassOptInRequired со ссылкой на класс аннотации:

@RequiresOptIn(
level = RequiresOptIn.Level.WARNING,
message = "Interfaces in this library are experimental"
)
annotation class UnstableApi()

@SubclassOptInRequired(UnstableApi::class)
interface CoreLibraryApi

В этом примере интерфейс CoreLibraryApi требует, чтобы пользователи выполнили opt-in, прежде чем реализовывать его. Выполнить opt-in можно так:

@OptIn(UnstableApi::class)
interface MyImplementation: CoreLibraryApi

Если вы используете аннотацию @SubclassOptInRequired, чтобы требовать opt-in, это требование не распространяется на внутренние или вложенные классы.

Пример использования аннотации @SubclassOptInRequired в API см. в интерфейсе SharedFlow из библиотеки kotlinx.coroutines.

Улучшено разрешение перегрузок функций с обобщенными типами

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

В результате поведение различалось в зависимости от того, были ли перегрузки функциями-членами или функциями-расширениями. Например:

class KeyValueStore<K, V> {
    fun store(key: K, value: V) {} // 1
    fun store(key: K, lazyValue: () -> V) {} // 2
}

fun <K, V> KeyValueStore<K, V>.storeExtension(key: K, value: V) {} // 1 
fun <K, V> KeyValueStore<K, V>.storeExtension(key: K, lazyValue: () -> V) {} // 2

fun test(kvs: KeyValueStore<String, Int>) {
    // Member functions
    kvs.store("", 1)    // Resolves to 1
    kvs.store("") { 1 } // Resolves to 2

    // Extension functions
    kvs.storeExtension("", 1)    // Resolves to 1
    kvs.storeExtension("") { 1 } // Doesn't resolve
}

В этом примере у класса KeyValueStore есть две перегрузки функции store(): одна принимает параметры-функции с обобщенными типами K и V, а другая — лямбда-функцию, возвращающую обобщенный тип V. Аналогичным образом существуют две перегрузки функции-расширения: storeExtension().

При вызове функции store() с лямбда-функцией и без нее компилятор правильно выбирал нужные перегрузки. Однако при вызове функции-расширения storeExtension() с лямбда-функцией компилятор не выбирал нужную перегрузку, поскольку ошибочно считал подходящими обе перегрузки.

Чтобы устранить эту проблему, мы добавили эвристическое правило, позволяющее компилятору отбрасывать возможную перегрузку, если параметр-функция с обобщенным типом не может принять лямбда-функцию на основании информации из другого аргумента. Это изменение обеспечивает одинаковое поведение функций-членов и функций-расширений и включено по умолчанию в Kotlin 2.1.0.

Улучшены проверки полноты выражений when с sealed-классами

В предыдущих версиях Kotlin компилятор требовал ветвь else в выражениях when для параметров типа с sealed-типами в качестве верхних границ, даже если были учтены все случаи в иерархии sealed class. В Kotlin 2.1.0 это поведение исправлено и улучшено: проверки полноты стали эффективнее, теперь можно удалять лишние ветви else, делая выражения when чище и понятнее.

Ниже показан пример этого изменения:

sealed class Result
object Error: Result()
class Success(val value: String): Result()

fun <T : Result> render(result: T) = when (result) {
    Error -> "Error!"
    is Success -> result.value
    // Requires no else branch
}

Компилятор Kotlin K2

В Kotlin 2.1.0 компилятор K2 обеспечивает большую гибкость при работе с проверками компилятора и предупреждениями, а также улучшенную поддержку плагина kapt.

Дополнительные проверки компилятора

В Kotlin 2.1.0 можно включить дополнительные проверки компилятора K2. Это дополнительные проверки объявлений, выражений и типов, обычно не критичные для компиляции, но полезные для проверки следующих случаев:

Тип проверки

Комментарий

REDUNDANT_NULLABLE

Вместо Boolean? используется Boolean??

PLATFORM_CLASS_MAPPED_TO_KOTLIN

Вместо kotlin.String используется java.lang.String

ARRAY_EQUALITY_OPERATOR_CAN_BE_REPLACED_WITH_EQUALS

Вместо arrayOf("").contentEquals(arrayOf("")) используется arrayOf("") == arrayOf("")

REDUNDANT_CALL_OF_CONVERSION_METHOD

Вместо 42 используется 42.toInt()

USELESS_CALL_ON_NOT_NULL

Вместо "" используется "".orEmpty()

REDUNDANT_SINGLE_EXPRESSION_STRING_TEMPLATE

Вместо string используется "$string"

UNUSED_ANONYMOUS_PARAMETER

В лямбда-выражении передан параметр, который нигде не используется

REDUNDANT_VISIBILITY_MODIFIER

Вместо class Klass используется public class Klass

REDUNDANT_MODALITY_MODIFIER

Вместо class Klass используется final class Klass

REDUNDANT_SETTER_PARAMETER_TYPE

Вместо set(value) используется set(value: Int)

CAN_BE_VAL

var local = 0 объявлена, но ей не присваивается новое значение; вместо нее можно использовать val local = 42

ASSIGNED_VALUE_IS_NEVER_READ

val local = 42 объявлена, но впоследствии в коде не используется

UNUSED_VARIABLE

val local = 0 объявлена, но в коде не используется

REDUNDANT_RETURN_UNIT_TYPE

Вместо fun foo() {} используется fun foo(): Unit {}

UNREACHABLE_CODE

Инструкция присутствует в коде, но никогда не может быть выполнена

Если условие проверки выполняется, компилятор выдаст предупреждение с предложением по устранению проблемы.

Дополнительные проверки по умолчанию отключены. Чтобы включить их, используйте параметр компилятора -Wextra в командной строке или укажите extraWarnings в блоке compilerOptions {} файла сборки Gradle:

// build.gradle.kts
kotlin {
    compilerOptions {
        extraWarnings.set(true)
    }
}

Подробнее о том, как задавать и использовать параметры компилятора, см. в разделе Параметры компилятора в плагине Kotlin Gradle.

Глобальное подавление предупреждений

В версии 2.1.0 в компиляторе Kotlin появилась долгожданная возможность — глобальное подавление предупреждений.

Теперь можно подавлять определенные предупреждения во всем проекте с помощью синтаксиса -Xsuppress-warning=WARNING_NAME в командной строке или атрибута freeCompilerArgs в блоке compilerOptions {} файла сборки.

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

// build.gradle.kts
kotlin {
    compilerOptions {
        extraWarnings.set(true)
        freeCompilerArgs.add("-Xsuppress-warning=CAN_BE_VAL")
    }
}

Если вы хотите подавить предупреждение, но не знаете его название, выберите элемент и нажмите значок лампочки (или используйте сочетание клавиш Cmd + Enter/Alt + Enter):

Warning name intention

Новый параметр компилятора пока имеет статус экспериментального. Также обратите внимание на следующее:

  • Подавление ошибок не допускается.

  • Если указать неизвестное имя предупреждения, компиляция завершится ошибкой.

  • Можно указать несколько предупреждений одновременно:

    kotlinc -Xsuppress-warning=NOTHING_TO_INLINE -Xsuppress-warning=NO_TAIL_CALLS_FOUND main.kt
    
    // build.gradle.kts
    kotlin {
        compilerOptions {
            freeCompilerArgs.addAll(
                listOf(
                    "-Xsuppress-warning=NOTHING_TO_INLINE",
                    "-Xsuppress-warning=NO_TAIL_CALLS_FOUND"
                )
            )
        }
    }
    

Улучшенная реализация K2 kapt

Плагин kapt для компилятора K2 (K2 kapt) находится в статусе Alpha. Он может измениться в любое время.

Будем благодарны за ваши отзывы в YouTrack.

В настоящее время проекты, использующие плагин kapt, по умолчанию работают с компилятором K1, который поддерживает версии Kotlin до 1.9 включительно.

В Kotlin 1.9.20 мы запустили экспериментальную реализацию плагина kapt с компилятором K2 (K2 kapt). Теперь мы улучшили внутреннюю реализацию K2 kapt, чтобы устранить технические проблемы и проблемы с производительностью.

Новая реализация K2 kapt не добавляет новых возможностей, но ее производительность значительно улучшена по сравнению с предыдущей реализацией K2 kapt. Кроме того, поведение плагина K2 kapt теперь гораздо ближе к поведению K1 kapt.

Чтобы использовать новую реализацию плагина K2 kapt, включите ее так же, как и предыдущую версию плагина K2 kapt. Добавьте следующий параметр в файл gradle.properties проекта:

kapt.use.k2=true

В будущих выпусках реализация K2 kapt будет включена по умолчанию вместо K1 kapt, поэтому включать ее вручную больше не потребуется.

До стабилизации новой реализации мы будем очень признательны за ваши отзывы.

Разрешение конфликтов перегрузок между беззнаковыми и непримитивными типами

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

Перегруженные функции-расширения

fun Any.doStuff() = "Any"
fun UByte.doStuff() = "UByte"

fun main() {
    val uByte: UByte = UByte.MIN_VALUE
    uByte.doStuff() // Overload resolution ambiguity before Kotlin 2.1.0
}

В предыдущих версиях вызов uByte.doStuff() приводил к неоднозначности, поскольку подходили оба расширения: Any и UByte.

Перегруженные функции верхнего уровня

fun doStuff(value: Any) = "Any"
fun doStuff(value: UByte) = "UByte"

fun main() {
    val uByte: UByte = UByte.MIN_VALUE
    doStuff(uByte) // Overload resolution ambiguity before Kotlin 2.1.0
}

Аналогичным образом вызов doStuff(uByte) был неоднозначным, поскольку компилятор не мог определить, какую версию использовать: Any или UByte. В версии 2.1.0 компилятор правильно обрабатывает такие случаи и устраняет неоднозначность, отдавая предпочтение более конкретному типу — в данном случае UByte.

Kotlin/JVM

Начиная с версии 2.1.0 компилятор может генерировать классы с байт-кодом Java 23.

Строгая обработка диагностик несоответствия nullability в JSpecify

В Kotlin 2.1.0 введена строгая обработка аннотаций nullability из org.jspecify.annotations, что повышает безопасность типов при взаимодействии с Java.

Изменения затрагивают следующие аннотации nullability:

  • org.jspecify.annotations.Nullable

  • org.jspecify.annotations.NonNull

  • org.jspecify.annotations.NullMarked

  • Устаревшие аннотации в org.jspecify.nullness (JSpecify 0.2 и более ранние версии)

Начиная с Kotlin 2.1.0, по умолчанию несоответствия nullability приводят к ошибкам вместо предупреждений. Благодаря этому такие аннотации, как @NonNull и @Nullable, учитываются при проверке типов, что предотвращает неожиданные проблемы с nullability во время выполнения.

Аннотация @NullMarked также влияет на nullability всех членов в пределах своей области действия, делая поведение более предсказуемым при работе с кодом Java, содержащим аннотации.

Ниже показан пример нового поведения по умолчанию:

// Java
import org.jspecify.annotations.*;
public class SomeJavaClass {
    @NonNull
    public String foo() { //...
    }

    @Nullable
    public String bar() { //...
    }
}
// Kotlin
fun test(sjc: SomeJavaClass) {
    // Accesses a non-null result, which is allowed
    sjc.foo().length

    // Raises an error in the default strict mode because the result is nullable
    // To avoid the error, use ?.length instead
    sjc.bar().length
}

Вы можете вручную задать уровень серьезности диагностик для этих аннотаций. Для этого используйте параметр компилятора -Xnullability-annotations и выберите режим:

  • ignore: игнорировать несоответствия nullability.

  • warning: выдавать предупреждения о несоответствиях nullability.

  • strict: выдавать ошибки при несоответствиях nullability (режим по умолчанию).

Подробнее см. в разделе Аннотации nullability.

Kotlin Multiplatform

В Kotlin 2.1.0 появилась базовая поддержка экспорта в Swift, а публиковать библиотеки Kotlin Multiplatform стало проще. Также были улучшены возможности Gradle: стабилизирован новый DSL для настройки параметров компилятора и представлена предварительная версия функции изолированных проектов.

Новый DSL Gradle для параметров компилятора в мультиплатформенных проектах получил статус стабильного

В Kotlin 2.0.0 мы представили новый экспериментальный DSL Gradle, упрощающий настройку параметров компилятора во всех мультиплатформенных проектах. В Kotlin 2.1.0 этот DSL получил статус стабильного.

Теперь общая конфигурация проекта состоит из трех уровней. Самый высокий — уровень расширения, далее идет уровень цели, а самый низкий — единица компиляции (обычно это задача компиляции):

Kotlin compiler options levels

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

Предварительная версия изолированных проектов Gradle в Kotlin Multiplatform

Эта функция является экспериментальной и в настоящее время находится на предварительной альфа-стадии в Gradle. Используйте ее только с Gradle версии 8.10 и исключительно для оценки. В любой момент функция может быть удалена или изменена.

Мы будем признательны за ваши отзывы о ней в YouTrack. Для использования функции необходимо явно включить ее (подробности ниже).

В Kotlin 2.1.0 можно попробовать функцию изолированных проектов Gradle в мультиплатформенных проектах.

Функция изолированных проектов в Gradle повышает производительность сборки, изолируя конфигурации отдельных проектов Gradle друг от друга. Логика сборки каждого проекта не может напрямую обращаться к изменяемому состоянию других проектов, поэтому проекты могут безопасно выполняться параллельно. Для поддержки этой функции мы внесли изменения в модель плагина Kotlin Gradle и хотели бы узнать о вашем опыте на этапе предварительной версии.

Включить новую модель плагина Kotlin Gradle можно двумя способами:

  • Вариант 1: Проверка совместимости без включения изолированных проектов — чтобы проверить совместимость с новой моделью плагина Kotlin Gradle, не включая функцию изолированных проектов, добавьте следующее свойство Gradle в файл gradle.properties проекта:

    # gradle.properties
    kotlin.kmp.isolated-projects.support=enable
    
  • Вариант 2: Проверка с включенными изолированными проектами — при включении функции изолированных проектов в Gradle плагин Kotlin Gradle автоматически настраивается для использования новой модели. Чтобы включить функцию изолированных проектов, задайте системное свойство. В этом случае добавлять в проект свойство Gradle для плагина Kotlin Gradle не нужно.

Базовая поддержка экспорта в Swift

Эта функция находится на ранней стадии разработки. В любой момент она может быть удалена или изменена. Для ее использования необходимо явно включить ее (подробности ниже); используйте ее только для оценки. Мы будем признательны за ваши отзывы в YouTrack.

Версия 2.1.0 — первый шаг к поддержке экспорта в Swift в Kotlin. Она позволяет экспортировать исходный код Kotlin непосредственно в интерфейс Swift без использования заголовков Objective-C. Это должно упростить мультиплатформенную разработку для целевых платформ Apple.

Базовая поддержка включает возможность:

  • Экспортировать несколько модулей Gradle непосредственно из Kotlin в Swift.

  • Задавать собственные имена модулей Swift с помощью свойства moduleName.

  • Настраивать правила сворачивания структуры пакетов с помощью свойства flattenPackage.

В качестве отправной точки для настройки экспорта в Swift можно использовать следующий файл сборки проекта:

// build.gradle.kts 
kotlin {

    iosX64()
    iosArm64()
    iosSimulatorArm64()

    @OptIn(ExperimentalSwiftExportDsl::class)
    swiftExport {
        // Root module name
        moduleName = "Shared"

        // Collapse rule
        // Removes package prefix from generated Swift code
        flattenPackage = "com.example.sandbox"

        // Export external modules
        export(project(":subproject")) {
            // Exported module name
            moduleName = "Subproject"
            // Collapse exported dependency rule
            flattenPackage = "com.subproject.library"
        }
    }
}

Также можно клонировать наш общедоступный пример, в котором экспорт в Swift уже настроен.

Компилятор автоматически создает все необходимые файлы (в том числе файлы swiftmodule, статическую библиотеку a, а также файлы заголовков и modulemap) и копирует их в каталог сборки приложения, доступный из Xcode.

Как включить экспорт в Swift

Помните, что эта функция находится на ранней стадии разработки.

В настоящее время экспорт в Swift работает в проектах, использующих прямую интеграцию для подключения фреймворка iOS к проекту Xcode. Это стандартная конфигурация для проектов Kotlin Multiplatform, созданных в Android Studio или с помощью веб-мастера.

Чтобы попробовать экспорт в Swift в своем проекте:

  1. Добавьте следующий параметр Gradle в файл gradle.properties:

    # gradle.properties
    kotlin.experimental.swift-export.enabled=true
    
  2. Откройте настройки проекта в Xcode.

  3. На вкладке Build Phases найдите этап Run Script с задачей embedAndSignAppleFrameworkForXcode.

  4. Измените скрипт: на этапе выполнения скрипта должна использоваться задача embedSwiftExportForXcode:

    ./gradlew :<Shared module name>:embedSwiftExportForXcode
    
    Add the Swift export script

Оставить отзыв об экспорте в Swift

В будущих выпусках Kotlin мы планируем расширить и стабилизировать поддержку экспорта в Swift. Оставьте свой отзыв в этой задаче YouTrack.

Возможность публиковать библиотеки Kotlin с любого хоста

Эта функция является экспериментальной. Для ее использования необходимо явно включить ее (подробности ниже); используйте ее только для оценки. Мы будем признательны за ваши отзывы в YouTrack.

Компилятор Kotlin создает артефакты .klib для публикации библиотек Kotlin. Ранее необходимые артефакты можно было получить с любого хоста, кроме целевых платформ Apple, для которых требовался компьютер Mac. Это накладывало особые ограничения на проекты Kotlin Multiplatform, предназначенные для iOS, macOS, tvOS и watchOS.

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

Как включить публикацию библиотек с любого хоста

Чтобы попробовать кросс-компиляцию в своем проекте, добавьте следующий бинарный параметр в файл gradle.properties:

# gradle.properties
kotlin.native.enableKlibsCrossCompilation=true

Эта функция пока является экспериментальной и имеет некоторые ограничения. Вам по-прежнему потребуется компьютер Mac, если:

  • В вашей библиотеке есть зависимость cinterop.

  • В проекте настроена интеграция с CocoaPods.

  • Вам нужно собрать или протестировать конечные бинарные файлы для целевых платформ Apple.

Оставить отзыв о публикации библиотек с любого хоста

В будущих выпусках Kotlin мы планируем стабилизировать эту функцию и усовершенствовать публикацию библиотек. Оставьте свой отзыв в нашем трекере задач YouTrack.

Дополнительные сведения см. в разделе Публикация мультиплатформенных библиотек.

Поддержка неупакованных klib

В Kotlin 2.1.0 появилась возможность создавать неупакованные файловые артефакты .klib. Это позволяет настраивать зависимости непосредственно от klib, не распаковывая их предварительно.

Это изменение также может повысить производительность, сократив время компиляции и компоновки в проектах Kotlin/Wasm, Kotlin/JS и Kotlin/Native.

Например, наши тесты производительности показывают, что общее время сборки проекта сокращается примерно на 3% при одной задаче компоновки и 10 задачах компиляции (проект собирает один исполняемый бинарный файл для нативной платформы, зависящий от 9 упрощенных проектов). Однако фактическое влияние на время сборки зависит как от количества подпроектов, так и от их размеров.

Как настроить проект

Теперь по умолчанию задачи компиляции и компоновки Kotlin настроены на использование новых неупакованных артефактов.

Если вы настроили собственную логику сборки для разрешения klib и хотите использовать новые неупакованные артефакты, необходимо явно указать предпочтительный вариант разрешения пакетов klib в файле сборки Gradle:

// build.gradle.kts
import org.jetbrains.kotlin.gradle.plugin.attributes.KlibPackaging
// ...
val resolvableConfiguration = configurations.resolvable("resolvable") {

    // For the new non-packed configuration:
    attributes.attribute(KlibPackaging.ATTRIBUTE, project.objects.named(KlibPackaging.NON_PACKED))

    // For the previous packed configuration:
    attributes.attribute(KlibPackaging.ATTRIBUTE, project.objects.named(KlibPackaging.PACKED))
}

Неупакованные файлы .klib создаются по тому же пути в каталоге сборки проекта, где раньше располагались упакованные файлы. Теперь упакованные klib находятся в каталоге build/libs.

Если атрибут не указан, используется упакованный вариант. Проверить список доступных атрибутов и вариантов можно с помощью следующей команды консоли:

./gradlew outgoingVariants

Мы будем признательны за ваши отзывы об этой функции в YouTrack.

Дальнейшее устаревание старой целевой платформы android

В Kotlin 2.1.0 предупреждение об устаревании старого имени целевой платформы android стало ошибкой.

В настоящее время для проектов Kotlin Multiplatform, предназначенных для Android, мы рекомендуем использовать параметр androidTarget. Это временное решение необходимо, чтобы освободить имя android для будущего плагина Android/KMP от Google.

Дополнительные инструкции по миграции будут опубликованы после выхода нового плагина. Новый DSL от Google станет предпочтительным способом поддержки целевой платформы Android в Kotlin Multiplatform.

Дополнительные сведения см. в руководстве по совместимости Kotlin Multiplatform.

Прекращена поддержка объявления нескольких целей одного типа

До Kotlin 2.1.0 в мультиплатформенных проектах можно было объявлять несколько целей одного типа. Однако из-за этого было сложно различать цели и эффективно поддерживать общие наборы исходного кода. В большинстве случаев лучше использовать более простую настройку, например отдельные проекты Gradle. Подробные инструкции и пример миграции см. в разделе Объявление нескольких похожих целей руководства по совместимости Kotlin Multiplatform.

В Kotlin 1.9.20 при объявлении нескольких целей одного типа в мультиплатформенных проектах появлялось предупреждение об устаревании. В Kotlin 2.1.0 это предупреждение стало ошибкой для всех целей, кроме Kotlin/JS. Чтобы узнать, почему цели Kotlin/JS являются исключением, см. эту задачу в YouTrack.

Kotlin/Native

В Kotlin 2.1.0 появилась поддержка целевой платформы iosArm64 получила более высокий уровень, улучшен процесс кэширования cinterop и внесены другие изменения.

iosArm64 получил статус цели уровня 1

Целевая платформа iosArm64, необходимая для разработки на Kotlin Multiplatform, получила статус цели уровня 1. Это наивысший уровень поддержки в компиляторе Kotlin/Native.

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

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

Обновление LLVM с версии 11.1.0 до 16.0.0

В Kotlin 2.1.0 мы обновили LLVM с версии 11.1.0 до 16.0.0. Новая версия содержит исправления ошибок и обновления безопасности. В некоторых случаях она также обеспечивает оптимизацию компилятора и ускоряет компиляцию.

Если в проекте используются целевые платформы Linux, обратите внимание: теперь компилятор Kotlin/Native по умолчанию использует компоновщик lld для всех целевых платформ Linux.

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

Изменения кэширования в cinterop

В Kotlin 2.1.0 мы изменили процесс кэширования cinterop. Теперь в нем не используется аннотация CacheableTask. Вместо этого рекомендуется использовать тип выходных данных cacheIf для кэширования результатов задачи.

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

Устаревание распределителя памяти mimalloc

В Kotlin 1.9.0 мы представили новый распределитель памяти, а в Kotlin 1.9.20 включили его по умолчанию. Новый распределитель разработан для повышения эффективности сборки мусора и производительности диспетчера памяти Kotlin/Native во время выполнения.

Новый распределитель памяти заменил предыдущий распределитель по умолчанию — mimalloc. Теперь в компиляторе Kotlin/Native mimalloc объявляется устаревшим.

Теперь можно удалить параметр компилятора -Xallocator=mimalloc из скриптов сборки. Если вы столкнетесь с проблемами, сообщите о них в наш трекер задач.

Дополнительные сведения о распределителе памяти и сборке мусора в Kotlin см. в разделе Управление памятью в Kotlin/Native.

Kotlin/Wasm

Kotlin/Wasm получил несколько обновлений, включая поддержку инкрементальной компиляции.

Поддержка инкрементальной компиляции

Ранее при изменении кода Kotlin набор инструментов Kotlin/Wasm должен был перекомпилировать всю кодовую базу.

Начиная с версии 2.1.0, для целей Wasm поддерживается инкрементальная компиляция. При выполнении задач разработки компилятор перекомпилирует только файлы, связанные с изменениями, внесёнными после последней компиляции, что заметно сокращает время компиляции.

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

В текущей конфигурации инкрементальная компиляция для целей Wasm по умолчанию отключена. Чтобы включить инкрементальную компиляцию, добавьте следующую строку в файл local.properties или gradle.properties проекта:

# gradle.properties
kotlin.incremental.wasm=true

Попробуйте инкрементальную компиляцию Kotlin/Wasm и поделитесь отзывом. Ваши замечания помогут скорее сделать эту функцию стабильной и включить её по умолчанию.

API браузера перенесены в отдельную библиотеку kotlinx-browser

Ранее объявления веб-API и связанные с целями служебные инструменты входили в стандартную библиотеку Kotlin/Wasm.

В этом выпуске объявления org.w3c.* перенесены из стандартной библиотеки Kotlin/Wasm в новую библиотеку kotlinx-browser. Эта библиотека также включает другие пакеты, связанные с веб-технологиями, например org.khronos.webgl, kotlin.dom и kotlinx.browser.

Такое разделение обеспечивает модульность и позволяет независимо обновлять API, связанные с веб-технологиями, вне цикла выпусков Kotlin. Кроме того, теперь стандартная библиотека Kotlin/Wasm содержит только объявления, доступные в любой среде JavaScript.

Чтобы использовать объявления из перенесённых пакетов, добавьте зависимость kotlinx-browser в файл конфигурации сборки проекта:

// build.gradle.kts
val wasmJsMain by getting {
    dependencies {
        implementation("org.jetbrains.kotlinx:kotlinx-browser:0.3")
    }
}

Улучшенная отладка Kotlin/Wasm

Ранее при отладке кода Kotlin/Wasm в веб-браузерах в интерфейсе отладки могло отображаться низкоуровневое представление значений переменных. Из-за этого часто было сложно отслеживать текущее состояние приложения.

Kotlin/Wasm old debugger

Чтобы улучшить отладку, в представление переменных добавлены пользовательские форматтеры. Реализация использует API пользовательских форматтеров, который поддерживается основными браузерами, такими как Firefox и браузеры на основе Chromium.

Теперь значения переменных можно отображать и находить в более понятном и удобном для пользователя виде.

Kotlin/Wasm improved debugger

Чтобы попробовать новые возможности отладки:

  1. Добавьте следующий параметр компилятора в параметры компилятора wasmJs {}:

    // build.gradle.kts
    kotlin {
        wasmJs {
            // ...
    
            compilerOptions {
                freeCompilerArgs.add("-Xwasm-debugger-custom-formatters")
            }
        }
    }
    
  2. Включите пользовательские форматтеры в браузере:

    • В Chrome DevTools эта настройка доступна в разделе Настройки | Параметры | Консоль:

      Enable custom formatters in Chrome
    • В Firefox DevTools эта настройка доступна в разделе Настройки | Дополнительные настройки:

      Enable custom formatters in Firefox

Уменьшение размера бинарных файлов Kotlin/Wasm

Размер бинарных файлов Wasm, созданных при производственных сборках, уменьшится до 30%, а производительность может немного повыситься. Это связано с тем, что параметры Binaryen --closed-world, --type-ssa и --type-merging теперь считаются безопасными для всех проектов Kotlin/Wasm и включены по умолчанию.

Улучшенное взаимодействие с массивами JavaScript в Kotlin/Wasm

Хотя стандартная библиотека Kotlin/Wasm предоставляет тип JsArray<T> для массивов JavaScript, не было прямого способа преобразовать JsArray<T> в собственные типы Kotlin Array или List.

Из-за этого приходилось создавать пользовательские функции для преобразования массивов, что усложняло взаимодействие между кодом Kotlin и JavaScript.

В этом выпуске появилась функция-адаптер, которая автоматически преобразует JsArray<T> в Array<T> и наоборот, упрощая работу с массивами.

Ниже приведён пример преобразования обобщённых типов: Kotlin List<T> и Array<T> в JavaScript JsArray<T>.

val list: List<JsString> =
    listOf("Kotlin", "Wasm").map { it.toJsString() }

// Uses .toJsArray() to convert List or Array to JsArray
val jsArray: JsArray<JsString> = list.toJsArray()

// Uses .toArray() and .toList() to convert it back to Kotlin types 
val kotlinArray: Array<JsString> = jsArray.toArray()
val kotlinList: List<JsString> = jsArray.toList()

Также доступны аналогичные методы для преобразования типизированных массивов в соответствующие типы Kotlin (например, IntArray и Int32Array). Подробную информацию и реализацию см. в репозитории kotlinx-browser.

Ниже приведён пример преобразования типизированных массивов: Kotlin IntArray в JavaScript Int32Array.

import org.khronos.webgl.*

    // ...

    val intArray: IntArray = intArrayOf(1, 2, 3)
    
    // Uses .toInt32Array() to convert Kotlin IntArray to JavaScript Int32Array
    val jsInt32Array: Int32Array = intArray.toInt32Array()
    
    // Uses toIntArray() to convert JavaScript Int32Array back to Kotlin IntArray
    val kotlinIntArray: IntArray = jsInt32Array.toIntArray()

Поддержка доступа к сведениям об исключениях JavaScript в Kotlin/Wasm

Ранее при возникновении исключения JavaScript в Kotlin/Wasm тип JsException предоставлял только общее сообщение без сведений об исходной ошибке JavaScript.

Начиная с Kotlin 2.1.0, можно настроить JsException так, чтобы он включал исходное сообщение об ошибке и трассировку стека, включив специальный параметр компилятора. Это предоставляет больше контекста для диагностики проблем, возникших в JavaScript.

Это поведение зависит от API WebAssembly.JSTag, который доступен только в некоторых браузерах:

  • Chrome: поддерживается начиная с версии 115

  • Firefox: поддерживается начиная с версии 129

  • Safari: пока не поддерживается

Чтобы включить эту функцию, отключённую по умолчанию, добавьте следующий параметр компилятора в файл build.gradle.kts:

// build.gradle.kts
kotlin {
    wasmJs {
        compilerOptions {
            freeCompilerArgs.add("-Xwasm-attach-js-exception")
        }
    }
}

Ниже приведён пример нового поведения:

external object JSON {
    fun <T: JsAny> parse(json: String): T
}

fun main() {
    try {
        JSON.parse("an invalid JSON")
    } catch (e: JsException) {
        println("Thrown value is: ${e.thrownValue}")
        // SyntaxError: Unexpected token 'a', "an invalid JSON" is not valid JSON

        println("Message: ${e.message}")
        // Message: Unexpected token 'a', "an invalid JSON" is not valid JSON

        println("Stacktrace:")
        // Stacktrace:

        // Prints the full JavaScript stack trace 
        e.printStackTrace()
    }
}

Если параметр -Xwasm-attach-js-exception включён, JsException предоставляет подробные сведения об ошибке JavaScript. Без этого параметра JsException содержит только общее сообщение о том, что при выполнении кода JavaScript было выброшено исключение.

Отказ от экспорта по умолчанию

В рамках перехода на именованные экспорты ранее при использовании импорта по умолчанию для экспортов Kotlin/Wasm в JavaScript в консоль выводилась ошибка.

В версии 2.1.0 импорты по умолчанию полностью удалены, чтобы обеспечить полную поддержку именованных экспортов.

При написании кода JavaScript для цели Kotlin/Wasm теперь необходимо использовать соответствующие именованные импорты вместо импортов по умолчанию.

Это изменение завершает цикл отказа от использования и перехода на именованные экспорты:

В версии 2.0.0: В консоль выводилось предупреждение о том, что экспорт сущностей по умолчанию устарел.

В версии 2.0.20: Возникала ошибка с требованием использовать соответствующий именованный импорт.

В версии 2.1.0: Использование импортов по умолчанию полностью удалено.

Настройки Node.js для отдельных подпроектов

Настроить Node.js для проекта можно, задав свойства класса NodeJsRootPlugin для rootProject. В версии 2.1.0 эти настройки можно задать для каждого подпроекта с помощью нового класса NodeJsPlugin. Ниже приведён пример задания определённой версии Node.js для подпроекта:

// build.gradle.kts
project.plugins.withType<org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsPlugin> {
    project.the<org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsEnvSpec>().version = "22.0.0"
}

Чтобы использовать новый класс для всего проекта, добавьте такой же код в блок allprojects {}:

// build.gradle.kts
allprojects {
    project.plugins.withType<org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsPlugin> {
        project.the<org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsEnvSpec>().version = "your Node.js version"
    }
}

Также можно использовать плагины соглашений Gradle, чтобы применить настройки к определённому набору подпроектов.

Kotlin/JS

Поддержка символов, не входящих в идентификаторы, в свойствах

Ранее Kotlin/JS не позволял использовать имена тестовых методов с пробелами, заключённые в обратные кавычки.

Также нельзя было обращаться к свойствам объектов JavaScript, содержащим символы, недопустимые в идентификаторах Kotlin, например дефисы или пробелы:

external interface Headers {
    var accept: String?

    // Invalid Kotlin identifier due to hyphen
    var `content-length`: String?
}

val headers: Headers = TODO("value provided by a JS library")
val accept = headers.accept
// Causes error due to the hyphen in property name
val length = headers.`content-length`

Это поведение отличалось от JavaScript и TypeScript, в которых к таким свойствам можно обращаться с помощью символов, не входящих в идентификаторы.

Начиная с Kotlin 2.1.0 эта функция включена по умолчанию. Теперь Kotlin/JS позволяет использовать обратные кавычки (``) и аннотацию @JsName для взаимодействия со свойствами JavaScript, содержащими символы, не входящие в идентификаторы, а также для задания имён тестовых методов.

Кроме того, с помощью аннотаций @JsName и @JsQualifier можно сопоставлять имена свойств Kotlin с соответствующими именами в JavaScript:

object Bar {
    val `property example`: String = "bar"
}

@JsQualifier("fooNamespace")
external object Foo {
    val `property example`: String
}

@JsExport
object Baz {
    val `property example`: String = "bar"
}

fun main() {
    // In JavaScript, this is compiled into Bar.property_example_HASH
    println(Bar.`property example`)
    // In JavaScript, this is compiled into fooNamespace["property example"]
    println(Foo.`property example`)
    // In JavaScript, this is compiled into Baz["property example"]
    println(Baz.`property example`)
}

Поддержка генерации стрелочных функций ES2015

В Kotlin 2.1.0 в Kotlin/JS появилась поддержка генерации стрелочных функций ES2015, таких как (a, b) => expression, вместо анонимных функций.

Использование стрелочных функций может уменьшить размер пакета проекта, особенно при использовании экспериментального режима -Xir-generate-inline-anonymous-functions. Это также позволяет привести сгенерированный код в большее соответствие с современным JavaScript.

Эта функция включена по умолчанию при выборе ES2015 в качестве целевой версии. Кроме того, её можно включить с помощью аргумента командной строки -Xes-arrow-functions.

Подробнее об ES2015 (ECMAScript 2015, ES6) читайте в официальной документации.

Улучшения Gradle

Kotlin 2.1.0 полностью совместим с Gradle версий от 7.6.3 до 8.6. Также поддерживаются версии Gradle от 8.7 до 8.10, за одним исключением. Если вы используете плагин Kotlin Multiplatform Gradle, в многоплатформенных проектах могут появляться предупреждения об устаревании при вызове функции withJava() для цели JVM. Мы планируем как можно скорее устранить эту проблему.

Дополнительную информацию см. в соответствующей задаче в YouTrack.

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

Минимальная поддерживаемая версия AGP повышена до 7.3.1

Начиная с Kotlin 2.1.0, минимальная поддерживаемая версия Android Gradle Plugin — 7.3.1.

Минимальная поддерживаемая версия Gradle повышена до 7.6.3

Начиная с Kotlin 2.1.0, минимальная поддерживаемая версия Gradle — 7.6.3.

Новый API для расширений плагина Kotlin Gradle

В Kotlin 2.1.0 представлен новый API, упрощающий создание собственных плагинов для настройки плагина Kotlin Gradle. В рамках этого изменения интерфейсы KotlinTopLevelExtension и KotlinTopLevelExtensionConfig объявлены устаревшими, а для авторов плагинов добавлены следующие интерфейсы:

Название

Описание

KotlinBaseExtension

Тип расширения DSL плагина для настройки общих параметров плагинов Kotlin JVM, Android и Multiplatform для всего проекта:

  • org.jetbrains.kotlin.jvm

  • org.jetbrains.kotlin.android

  • org.jetbrains.kotlin.multiplatform

KotlinJvmExtension

Тип расширения DSL плагина для настройки параметров плагина Kotlin JVM для всего проекта.

KotlinAndroidExtension

Тип расширения DSL плагина для настройки параметров плагина Kotlin Android для всего проекта.

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

configure<KotlinBaseExtension> {
    if (this is HasConfigurableKotlinCompilerOptions<*>) {
        with(compilerOptions) {
            if (this is KotlinJvmCompilerOptions) {
                jvmTarget.set(JvmTarget.JVM_17)
            }
        }
    }
}

В этом примере цель JVM настраивается на версию 17 как для проектов JVM, так и для проектов Android.

Чтобы настроить параметры компилятора только для проектов JVM, используйте KotlinJvmExtension:

configure<KotlinJvmExtension> {
    compilerOptions {
        jvmTarget.set(JvmTarget.JVM_17)
    }

    target.mavenPublication {
        groupId = "com.example"
        artifactId = "example-project"
        version = "1.0-SNAPSHOT"
    }
}

В этом примере цель JVM для проектов JVM также настраивается на версию 17. Кроме того, для проекта настраивается публикация Maven, чтобы его результаты сборки публиковались в репозитории Maven.

Использовать KotlinAndroidExtension можно точно так же.

Символы компилятора скрыты от API плагина Kotlin Gradle

Ранее KGP включал org.jetbrains.kotlin:kotlin-compiler-embeddable в список зависимостей времени выполнения, из-за чего внутренние символы компилятора становились доступны в пути к классам скрипта сборки. Эти символы предназначались только для внутреннего использования.

Начиная с Kotlin 2.1.0, KGP включает в свой JAR-файл подмножество файлов классов org.jetbrains.kotlin:kotlin-compiler-embeddable и постепенно удаляет их. Это изменение призвано предотвратить проблемы совместимости и упростить обслуживание KGP.

Если другие части логики сборки, например плагины вроде kotlinter, зависят от версии org.jetbrains.kotlin:kotlin-compiler-embeddable, отличной от включённой в KGP, это может привести к конфликтам и исключениям времени выполнения.

Чтобы предотвратить такие проблемы, теперь KGP выводит предупреждение, если org.jetbrains.kotlin:kotlin-compiler-embeddable находится в пути к классам сборки вместе с KGP.

В качестве долгосрочного решения мы рекомендуем авторам плагинов, использующим классы org.jetbrains.kotlin:kotlin-compiler-embeddable, запускать их в изолированном загрузчике классов. Например, этого можно добиться с помощью Gradle Workers API, используя изоляцию загрузчика классов или процесса.

Использование Gradle Workers API

В этом примере показано, как безопасно использовать компилятор Kotlin в проекте, создающем плагин Gradle. Сначала добавьте в скрипт сборки зависимость только для компиляции. Это сделает символ доступным только во время компиляции:

// build.gradle.kts
dependencies {
    compileOnly("org.jetbrains.kotlin:kotlin-compiler-embeddable:2.4.20")
}

Затем определите действие Gradle для вывода версии компилятора Kotlin:

import org.gradle.workers.WorkAction
import org.gradle.workers.WorkParameters
import org.jetbrains.kotlin.config.KotlinCompilerVersion
abstract class ActionUsingKotlinCompiler : WorkAction<WorkParameters.None> {
    override fun execute() {
        println("Kotlin compiler version: ${KotlinCompilerVersion.getVersion()}")
    }
}

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

import org.gradle.api.DefaultTask
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.tasks.Classpath
import org.gradle.api.tasks.TaskAction
import org.gradle.workers.WorkerExecutor
import javax.inject.Inject
abstract class TaskUsingKotlinCompiler: DefaultTask() {
    @get:Inject
    abstract val executor: WorkerExecutor

    @get:Classpath
    abstract val kotlinCompiler: ConfigurableFileCollection

    @TaskAction
    fun compile() {
        val workQueue = executor.classLoaderIsolation {
            classpath.from(kotlinCompiler)
        }
        workQueue.submit(ActionUsingKotlinCompiler::class.java) {}
    }
}

Наконец, настройте путь к классам компилятора Kotlin в плагине Gradle:

import org.gradle.api.Plugin
import org.gradle.api.Project
abstract class MyPlugin: Plugin<Project> {
    override fun apply(target: Project) {
        val myDependencyScope = target.configurations.create("myDependencyScope")
        target.dependencies.add(myDependencyScope.name, "$KOTLIN_COMPILER_EMBEDDABLE:$KOTLIN_COMPILER_VERSION")
        val myResolvableConfiguration = target.configurations.create("myResolvable") {
            extendsFrom(myDependencyScope)
        }
        target.tasks.register("myTask", TaskUsingKotlinCompiler::class.java) {
            kotlinCompiler.from(myResolvableConfiguration)
        }
    }

    companion object {
        const val KOTLIN_COMPILER_EMBEDDABLE = "org.jetbrains.kotlin:kotlin-compiler-embeddable"
        const val KOTLIN_COMPILER_VERSION = "2.4.20"
    }
}

Обновления компилятора Compose

Поддержка нескольких файлов конфигурации стабильности

Компилятор Compose может обрабатывать несколько файлов конфигурации стабильности, однако параметр stabilityConfigurationFile плагина Compose Compiler Gradle ранее позволял указать только один файл. В Kotlin 2.1.0 эта функциональность переработана, чтобы для одного модуля можно было использовать несколько файлов конфигурации стабильности:

  • Параметр stabilityConfigurationFile объявлен устаревшим.

  • Добавлен новый параметр stabilityConfigurationFiles типа ListProperty<RegularFile>.

Вот как передать компилятору Compose несколько файлов с помощью нового параметра:

// build.gradle.kt
composeCompiler {
    stabilityConfigurationFiles.addAll(
        project.layout.projectDirectory.file("configuration-file1.conf"),
        project.layout.projectDirectory.file("configuration-file2.conf"),
    )
}

Приостанавливаемая композиция

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

Чтобы попробовать приостанавливаемую композицию, добавьте следующий флаг функции в конфигурацию Gradle для компилятора Compose:

// build.gradle.kts
composeCompiler {
    featureFlags = setOf(
        ComposeFeatureFlag.PausableComposition
    )
}

Поддержка этой функции в среде выполнения добавлена в версию 1.8.0-alpha02 библиотеки androidx.compose.runtime. При использовании более старых версий среды выполнения флаг функции не действует.

Изменения в открытых и переопределённых функциях @Composable

Виртуальные функции @Composable (open, abstract и переопределённые) больше не могут быть перезапускаемыми. Генерация кода для перезапускаемых групп создавала вызовы, которые работали некорректно при наследовании и приводили к сбоям во время выполнения.

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

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

Ранее компилятор Compose создавал полную копию IR-модуля для преобразования типов @Composable. Помимо повышенного расхода памяти при копировании элементов, не связанных с Compose, такое поведение также нарушало работу нижестоящих плагинов компилятора в некоторых крайних случаях.

Операция копирования удалена, что может ускорить компиляцию.

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

Изменения уровня строгости устаревания API стандартной библиотеки

В Kotlin 2.1.0 мы повышаем уровень строгости устаревания нескольких API стандартной библиотеки: с предупреждения до ошибки. Если ваш код использует эти API, обновите его для обеспечения совместимости. Наиболее заметные изменения:

  • Функции преобразования регистра с учётом локали для Char и String объявлены устаревшими: Такие функции, как Char.toLowerCase(), Char.toUpperCase(), String.toUpperCase() и String.toLowerCase(), теперь объявлены устаревшими, а их использование приводит к ошибке. Замените их на функции, не зависящие от локали, или другие механизмы преобразования регистра. Если вы хотите продолжить использовать локаль по умолчанию, замените вызовы вроде String.toLowerCase() на String.lowercase(Locale.getDefault()), явно указав локаль. Для преобразования без учёта локали замените их на String.lowercase(), которая по умолчанию использует инвариантную локаль.

  • API заморозки Kotlin/Native объявлен устаревшим: Использование объявлений, связанных с заморозкой и ранее помеченных аннотацией @FreezingIsDeprecated, теперь приводит к ошибке. Это изменение отражает переход от устаревшего менеджера памяти Kotlin/Native, который требовал замораживать объекты для их совместного использования потоками. О том, как перейти с API заморозки на новую модель памяти, см. в руководстве по миграции Kotlin/Native. Дополнительную информацию см. в объявлении об отказе от заморозки.

  • appendln() объявлен устаревшим в пользу appendLine(): Функции StringBuilder.appendln() и Appendable.appendln() теперь объявлены устаревшими, а их использование приводит к ошибке. Вместо них используйте функции StringBuilder.appendLine() или Appendable.appendLine(). Функция appendln() объявлена устаревшей, поскольку в Kotlin/JVM она использует системное свойство line.separator, значение по умолчанию которого различается в разных ОС. В Kotlin/JVM по умолчанию для этого свойства используется \r\n (CR LF) в Windows и \n (LF) в других системах. В свою очередь, функция appendLine() всегда использует \n (LF) в качестве разделителя строк, обеспечивая одинаковое поведение на разных платформах.

Полный список затронутых API в этом выпуске см. в задаче KT-71628 в YouTrack.

Стабильные расширения обхода дерева файлов для java.nio.file.Path

В Kotlin 1.7.20 появились экспериментальные функции-расширения для класса java.nio.file.Path, позволяющие обходить дерево файлов. В Kotlin 2.1.0 следующие расширения для обхода дерева файлов получили статус стабильных:

  • walk() лениво обходит дерево файлов, корнем которого является указанный путь.

  • fileVisitor() позволяет отдельно создать FileVisitor. FileVisitor задаёт действия, выполняемые над каталогами и файлами во время обхода.

  • visitFileTree(fileVisitor: FileVisitor, ...) обходит дерево файлов, вызывая указанную FileVisitor для каждой обнаруженной записи, и использует под капотом функцию java.nio.file.Files.walkFileTree().

  • visitFileTree(..., builderAction: FileVisitorBuilder.() -> Unit) создаёт FileVisitor с переданной builderAction и вызывает функцию visitFileTree(fileVisitor, ...).

  • sealed interface FileVisitorBuilder позволяет определить собственную реализацию FileVisitor.

  • enum class PathWalkOption предоставляет параметры обхода для функции Path.walk().

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

Например, можно явно создать FileVisitor и использовать его позже:

val cleanVisitor = fileVisitor {
    onPreVisitDirectory { directory, attributes ->
        // Placeholder: Add logic on visiting directories
        FileVisitResult.CONTINUE
    }

    onVisitFile { file, attributes ->
        // Placeholder: Add logic on visiting files
        FileVisitResult.CONTINUE
    }
}

// Placeholder: Add logic here for general setup before traversal
projectDirectory.visitFileTree(cleanVisitor)

Также можно создать FileVisitor с помощью builderAction и сразу использовать его для обхода:

projectDirectory.visitFileTree {
    // Defines the builderAction:
    onPreVisitDirectory { directory, attributes ->
        // Some logic on visiting directories
        FileVisitResult.CONTINUE
    }

    onVisitFile { file, attributes ->
        // Some logic on visiting files
        FileVisitResult.CONTINUE
    }
}

Кроме того, дерево файлов, корнем которого является указанный путь, можно обойти с помощью функции walk():

fun traverseFileTree() {
    val cleanVisitor = fileVisitor {
        onPreVisitDirectory { directory, _ ->
            if (directory.name == "build") {
                directory.toFile().deleteRecursively()
                FileVisitResult.SKIP_SUBTREE
            } else {
                FileVisitResult.CONTINUE
            }
        }

        // Deletes files with the .class extension
        onVisitFile { file, _ ->
            if (file.extension == "class") {
                file.deleteExisting()
            }
            FileVisitResult.CONTINUE
        }
    }

    // Sets up the root directory and files
    val rootDirectory = createTempDirectory("Project")

    // Creates the src directory with A.kt and A.class files
    rootDirectory.resolve("src").let { srcDirectory ->
        srcDirectory.createDirectory()
        srcDirectory.resolve("A.kt").createFile()
        srcDirectory.resolve("A.class").createFile()
    }

    // Creates the build directory with a Project.jar file
    rootDirectory.resolve("build").let { buildDirectory ->
        buildDirectory.createDirectory()
        buildDirectory.resolve("Project.jar").createFile()
    }

    // Uses the walk() function:
    val directoryStructure = rootDirectory.walk(PathWalkOption.INCLUDE_DIRECTORIES)
        .map { it.relativeTo(rootDirectory).toString() }
        .toList().sorted()
    println(directoryStructure)
    // "[, build, build/Project.jar, src, src/A.class, src/A.kt]"
  
    // Traverses the file tree with cleanVisitor, applying the rootDirectory.visitFileTree(cleanVisitor) cleanup rules
    val directoryStructureAfterClean = rootDirectory.walk(PathWalkOption.INCLUDE_DIRECTORIES)
        .map { it.relativeTo(rootDirectory).toString() }
        .toList().sorted()
    println(directoryStructureAfterClean)
    // "[, src, src/A.kt]"
}

Обновления документации

В документации Kotlin появились заметные изменения:

Концепции языка

  • Обновлена страница Безопасность относительно null – узнайте, как безопасно обрабатывать значения null в коде.

  • Обновлена страница Объявления и выражения объектов – узнайте, как определить класс и создать его экземпляр за один шаг.

  • Обновлен раздел Выражения и операторы when – узнайте об условной конструкции when и о том, как ее использовать.

  • Обновлены страницы План развития Kotlin, Принципы развития Kotlin и Возможности языка Kotlin и предложения – узнайте о планах Kotlin, текущих разработках и руководящих принципах.

Компилятор Compose

  • Документация по компилятору Compose теперь находится в разделе «Компилятор и плагины» – узнайте о компиляторе Compose, параметрах компилятора и шагах миграции.

Справочники API

  • Новый справочник API плагинов Kotlin Gradle – изучите справочники API плагина Kotlin Gradle и плагина Compose compiler Gradle.

Мультиплатформенная разработка

  • Новая страница Создание библиотеки Kotlin для мультиплатформенной разработки – узнайте, как проектировать библиотеки Kotlin для Kotlin Multiplatform.

  • Новая страница Введение в Kotlin Multiplatform – узнайте об основных концепциях Kotlin Multiplatform, зависимостях, библиотеках и многом другом.

  • Новый раздел Интеграция с iOS – узнайте, как интегрировать общий модуль Kotlin Multiplatform в приложение для iOS.

  • Новая страница Файл описания Kotlin/Native – узнайте, как создать файл описания для использования библиотек C и Objective-C.

  • Начало работы с WASI – узнайте, как запускать простое приложение Kotlin/Wasm с помощью WASI в различных виртуальных машинах WebAssembly.

Инструменты

  • Новое руководство по миграции на Dokka – узнайте, как перейти на плагин Dokka Gradle v2.

Руководство по совместимости для Kotlin 2.1.0

Kotlin 2.1.0 — это выпуск с новыми возможностями, поэтому в нем могут появиться изменения, несовместимые с кодом, написанным для более ранних версий языка. Подробный список этих изменений приведен в руководстве по совместимости для Kotlin 2.1.0.

Установка Kotlin 2.1.0

Начиная с IntelliJ IDEA 2023.3 и Android Studio Iguana (2023.2.1) Canary 15, плагин Kotlin распространяется как встроенный плагин, включенный в вашу IDE. Это означает, что установить плагин из JetBrains Marketplace больше нельзя.

Чтобы обновить Kotlin до новой версии, измените версию Kotlin на 2.1.0 в сценариях сборки.

23 марта 2026 г.
Что нового в Kotlin 2.1.20Руководство по совместимости для Kotlin 2.1.x

© 2010–2026 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/whatsnew21.html

Spec-Zone.ru

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