Spec-Zone.ru › Kotlin 1.8

Что нового в 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 теперь можно захватить when объект в переменную:

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

Хотя это уже было возможно, выделить эту переменную непосредственно перед when, val в when имеет правильно ограниченную область действия внутри тела when, предотвращая загрязнение пространства имён. Полная документация по when здесь.

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

С Kotlin 1.3 возможно помечать члены объекта companion интерфейсов аннотациями @JvmStatic и @JvmField. В файле .class такие члены будут подняты до соответствующего интерфейса и помечены как static.

Например, следующий код 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
    }
}

Главная функция без параметров

По соглашению, точкой входа в программу Kotlin является функция с сигнатурой типа main(args: Array<String>), где args представляют аргументы командной строки, переданные программе. Однако не каждое приложение поддерживает аргументы командной строки, поэтому этот параметр часто оказывается неиспользуемым.

Kotlin 1.3 ввёл более простую форму main без параметров. Теперь "Hello, World" на Kotlin короче на 19 символов!

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

Функции с большой арностью

В Kotlin функциональные типы представлены в виде обобщённых классов, принимающих разное количество параметров: Function0<R>, Function1<P0, R>, Function2<P0, P1, R>, ... Этот подход имеет проблему, заключающуюся в том, что этот список конечен, и в настоящее время он заканчивается Function22.

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 вводит режим поэтапного компилятора, который можно включить, передав аргумент -progressive в компилятор.

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

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

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

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

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

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

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

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

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

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

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

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
}

Предупреждение! Пометка вашего API аннотацией @JvmDefault имеет серьезные последствия для бинарной совместимости. Перед использованием @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
}

Расширения isNullOrEmpty и 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 Compiler, в Kotlin 1.3 он всё ещё считается экспериментальной функцией.

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

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

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

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

Поддержка скетчей

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

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

Последнее изменение: 10 января 2023 г.
Что нового в Kotlin 1.4 Что нового в Kotlin 1.2

© 2010–2023 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