Spec-Zone.ru › Kotlin 1.7

Что нового в Kotlin 1.3

Дата выхода: 29 октября 2018 года

Выпуск сопроцедур

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

Kotlin 1.3 вводит вызываемые ссылки на приостанавливаемые функции и поддержку сопроцедур в API рефлексии.

Kotlin/Native

Kotlin 1.3 продолжает улучшать и дорабатывать целевую платформу Native. Подробности см. в обзоре Kotlin/Native.

Многоплатформенные проекты

В версии 1.3 мы полностью переработали модель многоплатформенных проектов, чтобы повысить выразительность и гибкость, а также упростить совместное использование общего кода. Кроме того, Kotlin/Native теперь поддерживается как одна из целей!

Ключевые различия с прежней моделью:

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

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

  • Изменена конфигурация зависимостей; теперь зависимости указываются отдельно для каждого корня исходного кода.

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

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

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

Контракты

Компилятор Kotlin выполняет обширный статический анализ, чтобы предоставлять предупреждения и уменьшать объём кода. Одна из наиболее заметных особенностей — это интеллектуальные преобразования типов — с возможностью автоматического преобразования типа на основе выполненных проверок типа:

fun foo(s: String?) {
    if (s != null) s.length // Compiler automatically casts 's' to 'String'
}

Однако, как только эти проверки извлекаются в отдельную функцию, все интеллектуальные преобразования типов сразу исчезают:

fun String?.isNotNull(): Boolean = this != null

fun foo(s: String?) {
    if (s.isNotNull()) s.length // No smartcast :(
}

Чтобы улучшить поведение в таких случаях, Kotlin 1.3 вводит экспериментальный механизм, называемый контрактами.

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

  • Улучшение анализа интеллектуальных преобразований типов путём объявления связи между результатом вызова функции и значениями переданных аргументов:

fun require(condition: Boolean) {
    // This is a syntax form which tells the compiler:
    // "if this function returns successfully, then the passed 'condition' is true"
    contract { returns() implies condition }
    if (!condition) throw IllegalArgumentException(...)
}

fun foo(s: String?) {
    require(s is String)
    // s is smartcast to 'String' here, because otherwise
    // 'require' would have thrown an exception
}
  • Улучшение анализа инициализации переменных при наличии функций высшего порядка:

fun synchronize(lock: Any?, block: () -> Unit) {
    // It tells the compiler:
    // "This function will invoke 'block' here and now, and exactly one time"
    contract { callsInPlace(block, EXACTLY_ONCE) }
}

fun foo() {
    val x: Int
    synchronize(lock) {
        x = 42 // Compiler knows that lambda passed to 'synchronize' is called
               // exactly once, so no reassignment is reported
    }
    println(x) // Compiler knows that lambda will be definitely called, performing
               // initialization, so 'x' is considered to be initialized here
}

Контракты в stdlib

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

//sampleStart
fun bar(x: String?) {
    if (!x.isNullOrEmpty()) {
        println("length of '$x' is ${x.length}") // Yay, smartcast to not-null!
    }
}
//sampleEnd
fun main() {
    bar(null)
    bar("42")
}

Пользовательские контракты

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

Пользовательские контракты вводятся с помощью вызова функции contract stdlib, которая предоставляет область действия DSL:

fun String?.isNullOrEmpty(): Boolean {
    contract {
        returns(false) implies (this@isNullOrEmpty != null)
    }
    return this == null || isEmpty()
}

Подробности синтаксиса, а также информация о совместимости см. в KEEP.

Переменная, содержащая объект when

В Kotlin 1.3 теперь можно получить доступ к объекту <%%CODE_BLOCK_9%%%> в переменную:

fun Request.getBody() =
        when (val response = executeRequest()) {
            is Success -> response.body
            is HttpError -> throw HttpException(response.status)
        }

Хотя это уже было возможно сделать с использованием объекта <%%CODE_BLOCK_11%%>, <%%CODE_BLOCK_12%%%> в <%%CODE_BLOCK_13%%%> имеет соответствующий объём, ограниченный телом <%%CODE_BLOCK_14%%>, предотвращая загрязнение пространства имён. Полная документация по <%%CODE_BLOCK_15%%%> здесь.

@JvmStatic и @JvmField в компаньонах интерфейсов

В Kotlin 1.3 можно помечать члены <%%CODE_BLOCK_16%%%> объекта интерфейсов аннотациями <%%CODE_BLOCK_17%%%> и <%%CODE_BLOCK_18%%>. В файле с байткодом такие члены будут перенесены в соответствующий интерфейс и помечены как <%%CODE_BLOCK_19%%>.

Например, следующий код Kotlin:

interface Foo {
    companion object {
        @JvmField
        val answer: Int = 42

        @JvmStatic
        fun sayHello() {
            println("Hello, world!")
        }
    }
}

Эквивалентен следующему коду Java:

interface Foo {
    public static int answer = 42;
    public static void sayHello() {
        // ...
    }
}

Вложенные объявления в аннотационных классах

В Kotlin 1.3 аннотации могут содержать вложенные классы, интерфейсы, объекты и компаньоны:

annotation class Foo {
    enum class Direction { UP, DOWN, LEFT, RIGHT }
    
    annotation class Bar

    companion object {
        fun foo(): Int = 42
        val bar: Int = 42
    }
}

Безпараметричный main

По соглашению, точкой входа в программу Kotlin является функция с сигнатурой, подобной <%%CODE_BLOCK_23%%>, где <%%CODE_BLOCK_24%%> представляют аргументы командной строки, передаваемые в программу. Однако не каждое приложение поддерживает аргументы командной строки, поэтому этот параметр часто не используется.

Kotlin 1.3 ввёл более простой вариант <%%CODE_BLOCK_25%%>, который не принимает параметров. Теперь "Hello, World" на Kotlin короче на 19 символов!

fun main() {
    println("Hello, world!")
}

Функции с большим числом аргументов

В Kotlin функциональные типы представляются как классы дженериков, принимающие разное число параметров: <%%CODE_BLOCK_27%%>, <%%CODE_BLOCK_28%%>, <%%CODE_BLOCK_29%%>, ... Такой подход имеет проблему, состоящую в том, что этот список конечный, и в настоящее время заканчивается <%%CODE_BLOCK_30%%%.

Kotlin 1.3 снимает это ограничение и добавляет поддержку функций с большим числом аргументов:

fun trueEnterpriseComesToKotlin(block: (Any, Any, ... /* 42 more */, Any) -> Any) {
    block(Any(), Any(), ..., Any())
}

Постепенный режим

Kotlin уделяет большое внимание стабильности и обратной совместимости кода: политика совместимости Kotlin гласит, что изменения, нарушающие обратную совместимость (например, изменение, из-за которого код, который раньше компилировался, больше не компилируется), могут быть внесены только в основных выпусках (1.2, 1.3 и т. д.).

Мы считаем, что многие пользователи могли бы использовать гораздо более быстрый цикл, когда критические исправления ошибок компилятора поступают немедленно, делая код более безопасным и корректным. Поэтому Kotlin 1.3 вводит постепенный режим компилятора, который можно включить, передав аргумент <%%CODE_BLOCK_32%%> компилятору.

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

  • Они сохраняют обратную совместимость исходного кода со старыми компиляторами, что означает, что весь код, который компилируется постепенным компилятором, будет успешно компилироваться и обычным компилятором.

  • Они только делают код более безопасным в некотором смысле — например, некоторые некорректные интеллектуальные преобразования типов могут быть запрещены, поведение сгенерированного кода может быть изменено для большей предсказуемости/стабильности и так далее.

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

Встроенные классы

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

Kotlin 1.3 вводит новый тип объявления — <%%CODE_BLOCK_33%%>. Встроенные классы можно рассматривать как ограниченную версию обычных классов, в частности, встроенные классы должны иметь ровно одно свойство:

inline class Name(val s: String)

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

inline class Name(val s: String)
//sampleStart
fun main() {
    // In the next line no constructor call happens, and
    // at the runtime 'name' contains just string "Kotlin"
    val name = Name("Kotlin")
    println(name.s) 
}
//sampleEnd

См. справочное руководство по встроенным классам для получения дополнительной информации.

END_OF_DOCUMENT_MARKER ```

Целочисленные значения без знака

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

Kotlin 1.3 вводит типы целочисленных значений без знака:

  • kotlin.UByte: целое беззнаковое 8-битное число, диапазон от 0 до 255

  • kotlin.UShort: целое беззнаковое 16-битное число, диапазон от 0 до 65535

  • kotlin.UInt: целое беззнаковое 32-битное число, диапазон от 0 до 232 - 1

  • kotlin.ULong: целое беззнаковое 64-битное число, диапазон от 0 до 264 - 1

Большинство функций для знаковых типов также поддерживаются для беззнаковых аналогов:

fun main() {
//sampleStart
// You can define unsigned types using literal suffixes
val uint = 42u 
val ulong = 42uL
val ubyte: UByte = 255u

// You can convert signed types to unsigned and vice versa via stdlib extensions:
val int = uint.toInt()
val byte = ubyte.toByte()
val ulong2 = byte.toULong()

// Unsigned types support similar operators:
val x = 20u + 22u
val y = 1u shl 8
val z = "128".toUByte()
val range = 1u..5u
//sampleEnd
println("ubyte: $ubyte, byte: $byte, ulong2: $ulong2")
println("x: $x, y: $y, z: $z, range: $range")
}

Подробную информацию см. в справочнике.

@JvmDefault

@JvmDefault находится в стадии Экспериментальной. Его могут удалить или изменить в любое время. Используйте его только в оценочных целях. Мы ценим ваши отзывы об этом в YouTrack.

Kotlin ориентируется на широкий спектр версий Java, включая Java 6 и Java 7, где методы по умолчанию в интерфейсах не разрешены. Для удобства компилятор Kotlin обходит это ограничение, но это решение несовместимо с методами default , введёнными в Java 8.

Это может быть проблемой для межплатформенной работы с Java, поэтому Kotlin 1.3 вводит аннотацию @JvmDefault. Методы, помеченные этой аннотацией, будут сгенерированы как методы default для JVM:

interface Foo {
    // Will be generated as 'default' method
    @JvmDefault
    fun foo(): Int = 42
}

Предупреждение! Добавление аннотации @JvmDefault к вашему API имеет серьезные последствия для бинарной совместимости. Перед использованием @JvmDefault в рабочей среде обязательно ознакомьтесь со страницей справки.

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

Многоплатформенный генератор случайных чисел

До Kotlin 1.3 не было единого способа генерировать случайные числа на всех платформах — нам приходилось прибегать к платформенно-зависимым решениям, таким как java.util.Random на JVM. Этот релиз исправляет эту проблему, представив класс kotlin.random.Random, доступный на всех платформах:

import kotlin.random.Random

fun main() {
//sampleStart
    val number = Random.nextInt(42)  // number is in range [0, limit)
    println(number)
//sampleEnd
}

Расширения isNullOrBlank и orEmpty

Расширения isNullOrEmpty и orEmpty для некоторых типов уже присутствуют в stdlib. Первое возвращает true, если получатель null или пустой, а второе возвращает пустой экземпляр, если получатель null. Kotlin 1.3 предоставляет аналогичные расширения для коллекций, карт и массивов объектов.

Копирование элементов между двумя существующими массивами

Функции array.copyInto(targetArray, targetOffset, startIndex, endIndex) для существующих типов массивов, включая массивы без знака, упрощают реализацию контейнеров на основе массивов в чистом Kotlin.

fun main() {
//sampleStart
    val sourceArr = arrayOf("k", "o", "t", "l", "i", "n")
    val targetArr = sourceArr.copyInto(arrayOfNulls<String>(6), 3, startIndex = 3, endIndex = 6)
    println(targetArr.contentToString())
    
    sourceArr.copyInto(targetArr, startIndex = 0, endIndex = 3)
    println(targetArr.contentToString())
//sampleEnd
}

associateWith

Часто возникает ситуация, когда у вас есть список ключей и вам нужно создать карту, сопоставив каждый из этих ключей с некоторым значением. Раньше это можно было сделать с помощью функции associate { it to getValue(it) }, но теперь мы предлагаем более эффективный и удобный для исследования альтернативный вариант: keys.associateWith { getValue(it) }.

fun main() {
//sampleStart
    val keys = 'a'..'f'
    val map = keys.associateWith { it.toString().repeat(5).capitalize() }
    map.forEach { println(it) }
//sampleEnd
}

Функции ifEmpty и ifBlank

Коллекции, карты, массивы объектов, последовательности символов и последовательности теперь имеют функцию ifEmpty, которая позволяет указать значение по умолчанию, которое будет использоваться вместо получателя, если он пустой:

fun main() {
//sampleStart
    fun printAllUppercase(data: List<String>) {
        val result = data
        .filter { it.all { c -> c.isUpperCase() } }
            .ifEmpty { listOf("<no uppercase>") }
        result.forEach { println(it) }
    }
    
    printAllUppercase(listOf("foo", "Bar"))
    printAllUppercase(listOf("FOO", "BAR"))
//sampleEnd
}

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

fun main() {
//sampleStart
    val s = "    \n"
    println(s.ifBlank { "<blank>" })
    println(s.ifBlank { null })
//sampleEnd
}

Закрытые классы в рефлексии

Мы добавили новый API к kotlin-reflect, который может использоваться для перечисления всех непосредственных подтипов класса sealed, а именно KClass.sealedSubclasses.

Незначительные изменения

  • Тип Boolean теперь имеет компаньон.

  • Расширение Any?.hashCode() , которое возвращает 0 для null.

  • Char теперь предоставляет константы MIN_VALUE и MAX_VALUE.

  • Константы SIZE_BYTES и SIZE_BITS в компаньонах примитивных типов.

Средства разработки

Поддержка стиля кода в IDE

Kotlin 1.3 добавляет поддержку рекомендуемого стиля кода в IntelliJ IDEA. См. эту страницу для руководства по миграции.

kotlinx.serialization

kotlinx.serialization — это библиотека, предоставляющая поддержку многоплатформенного (де)сериализации объектов в Kotlin. Раньше это был отдельный проект, но с Kotlin 1.3 он поставляется с дистрибутивом компилятора Kotlin наравне с другими плагинами компилятора. Основное отличие заключается в том, что вам не нужно вручную следить за тем, чтобы плагин Serialization IDE был совместим с версией плагина Kotlin IDE, которую вы используете: теперь плагин Kotlin IDE уже включает сериализацию!

См. подробности здесь.

Несмотря на то, что kotlinx.serialization теперь поставляется с дистрибутивом компилятора Kotlin, в Kotlin 1.3 он по-прежнему считается экспериментальной функцией.

Обновление скриптового API

Скриптинг находится в стадии Экспериментальной. Его могут удалить или изменить в любое время. Используйте его только в оценочных целях. Мы ценим ваши отзывы об этом в YouTrack.

Kotlin 1.3 продолжает развивать и улучшать API скриптирования, добавляя экспериментальную поддержку настройки скриптов, например, добавления внешних свойств, предоставления статических или динамических зависимостей и т. д.

Дополнительные сведения см. в KEEP-75.

Поддержка эскизов

Kotlin 1.3 вводит поддержку исполняемых Kotlin-файлов-эскизов. Файл-эскиз — это Kotlin-скрипт-файл с расширением .kts, который вы можете запустить и получить результаты вычисления непосредственно в редакторе.

Для получения подробной информации см. общую документацию по эскизам.

Последнее изменение: 06 сентября 2022 г.
Что нового в Kotlin 1.4 Что нового в Kotlin 1.2

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

Spec-Zone.ru

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