Spec-Zone.ru › Kotlin 1.7

Что нового в Kotlin 1.2

Дата выхода: 28 ноября 2017 года

Содержание

  • Проекты кросс-платформ

  • Другие возможности языка

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

  • JVM-бекенд

  • JavaScript-бекенд

Проекты кросс-платформ (экспериментальная возможность)

Проекты кросс-платформ — новая экспериментальная возможность Kotlin 1.2, позволяющая повторно использовать код между целевыми платформами, поддерживаемыми Kotlin — JVM, JavaScript и (в будущем) Native. В проекте кросс-платформ есть три типа модулей:

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

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

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

При компиляции проекта кросс-платформ для конкретной платформы генерируется код как для общей части, так и для части, специфичной для платформы.

Ключевой особенностью поддержки проектов кросс-платформ является возможность выражения зависимостей общего кода от платформенно-специфичных частей через объявления expected и actual. Объявление expected определяет API (класс, интерфейс, аннотация, объявление верхнего уровня и т. д.). Объявление actual представляет собой либо платформенно-зависимую реализацию API, либо псевдоним типа, ссылающийся на существующую реализацию API во внешней библиотеке. Вот пример:

В общем коде:

// expected platform-specific API:
expect fun hello(world: String): String

fun greet() {
    // usage of the expected API:
    val greeting = hello("multiplatform world")
    println(greeting)
}

expect class URL(spec: String) {
    open fun getHost(): String
    open fun getPath(): String
}

В коде JVM-платформы:

actual fun hello(world: String): String =
    "Hello, $world, on the JVM platform!"

// using existing platform-specific implementation:
actual typealias URL = java.net.URL

Подробности и шаги по созданию проекта кросс-платформ см. в документации по программированию кросс-платформ.

Другие возможности языка

Массивы-литералы в аннотациях

Начиная с Kotlin 1.2, аргументы массивов для аннотаций можно передавать с помощью нового синтаксиса массивов-литералов вместо функции arrayOf.

@CacheConfig(cacheNames = ["books", "default"])
public class BookRepositoryImpl {
    // ...
}

Синтаксис массивов-литералов ограничен аргументами аннотаций.

Свойства и локальные переменные lateinit для свойств верхнего уровня

Модификатор lateinit теперь можно использовать для свойств верхнего уровня и локальных переменных. Последние можно использовать, например, когда лямбда, переданная как аргумент конструктора одному объекту, ссылается на другой объект, который должен быть определен позже:

class Node<T>(val value: T, val next: () -> Node<T>)

fun main(args: Array<String>) {
    // A cycle of three nodes:
    lateinit var third: Node<Int>

    val second = Node(2, next = { third })
    val first = Node(1, next = { second })

    third = Node(3, next = { first })

    val nodes = generateSequence(first) { it.next() }
    println("Values in the cycle: ${nodes.take(7).joinToString { it.value.toString() }}, ...")
}

Проверка инициализации lateinit-переменной

Теперь можно проверить, была ли инициализирована переменная lateinit, используя isInitialized для ссылки на свойство:

class Foo {
    lateinit var lateinitVar: String

    fun initializationLogic() {
//sampleStart
        println("isInitialized before assignment: " + this::lateinitVar.isInitialized)
        lateinitVar = "value"
        println("isInitialized after assignment: " + this::lateinitVar.isInitialized)
//sampleEnd
    }
}

fun main(args: Array<String>) {
	Foo().initializationLogic()
}

Функции inline с параметрами лямбда по умолчанию

Функции inline теперь могут иметь значения по умолчанию для своих встроенных функциональных параметров:

//sampleStart
inline fun <E> Iterable<E>.strings(transform: (E) -> String = { it.toString() }) =
    map { transform(it) }

val defaultStrings = listOf(1, 2, 3).strings()
val customStrings = listOf(1, 2, 3).strings { "($it)" } 
//sampleEnd

fun main(args: Array<String>) {
    println("defaultStrings = $defaultStrings")
    println("customStrings = $customStrings")
}

Информация из явных преобразований типов используется для вывода типов

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

Это особенно важно для разработчиков Android, поскольку компилятор теперь может правильно анализировать вызовы обобщённых findViewById в Android API уровня 26:

val button = findViewById(R.id.button) as Button

Улучшения умных приведений типов

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

fun countFirst(s: Any): Int {
//sampleStart
    val firstChar = (s as? CharSequence)?.firstOrNull()
    if (firstChar != null)
    return s.count { it == firstChar } // s: Any is smart cast to CharSequence

    val firstItem = (s as? Iterable<*>)?.firstOrNull()
    if (firstItem != null)
    return s.count { it == firstItem } // s: Any is smart cast to Iterable<*>
//sampleEnd
    return -1
}

fun main(args: Array<String>) {
  val string = "abacaba"
  val countInString = countFirst(string)
  println("called on \"$string\": $countInString")

  val list = listOf(1, 2, 3, 1, 2)
  val countInList = countFirst(list)
  println("called on $list: $countInList")
}

Также, умные приведения в лямбда-выражениях теперь разрешены для локальных переменных, которые изменяются только до лямбды:

fun main(args: Array<String>) {
//sampleStart
    val flag = args.size == 0
    var x: String? = null
    if (flag) x = "Yahoo!"

    run {
        if (x != null) {
            println(x.length) // x is smart cast to String
        }
    }
//sampleEnd
}

Поддержка ::foo в качестве сокращения для this::foo

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

Изменение: корректные умные приведения после блоков try

Ранее Kotlin использовал присваивания внутри блока try для умных приведений после блока, что могло нарушить тип и безопасность от null и привести к ошибкам во время выполнения. Этот релиз исправляет эту проблему, делая умные приведения более строгими, но ломает некоторые фрагменты кода, которые полагались на такие умные приведения.

Чтобы переключиться на старое поведение умных приведений, передайте флаг отката -Xlegacy-smart-cast-after-try как аргумент компилятора. Он будет устаревшим в Kotlin 1.3.

Устаревание: наследование data-классов, переопределяющих copy

Когда data-класс наследуется от типа, у которого уже была функция copy с тем же сигналом, реализация copy для data-класса использовала значения по умолчанию из супертипа, что приводило к неинтуитивному поведению или аварийной остановке выполнения, если в супертипе не было параметров по умолчанию.

Наследование, которое приводит к конфликту copy, устарело с предупреждением в Kotlin 1.2 и станет ошибкой в Kotlin 1.3.

Устаревание: вложенные типы в записях перечисления

Определение вложенного типа, который не является inner class, внутри записей перечисления устарело из-за проблем с логикой инициализации. Это приводит к предупреждению в Kotlin 1.2 и станет ошибкой в Kotlin 1.3.

Устаревание: единственный именованный аргумент для vararg

Для согласованности с массивами-литералами в аннотациях, передача одного элемента для параметра vararg в именованной форме (foo(items = i)) устарела. Используйте оператор распространения с соответствующими функциями создания массивов:

foo(items = *arrayOf(1))

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

Устаревание: внутренние классы обобщённых классов, расширяющих Throwable

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

Устаревание: изменение вспомогательного поля только для чтения

Изменение вспомогательного поля только для чтения путём присваивания field = ... в пользовательском методе-геттере устарело, с предупреждением в Kotlin 1.2 и станет ошибкой в Kotlin 1.3.

END_OF_DOCUMENT_MARKER

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

Артефакты стандартной библиотеки Kotlin и разделенные пакеты

Стандартная библиотека Kotlin теперь полностью совместима с системой модулей Java 9, которая запрещает разделенные пакеты (несколько jar-файлов, объявляющих классы в одном и том же пакете). Для поддержки этого введены новые артефакты kotlin-stdlib-jdk7 и kotlin-stdlib-jdk8, которые заменяют старые kotlin-stdlib-jre7 и kotlin-stdlib-jre8.

Объявления в новых артефактах видны под теми же именами пакетов с точки зрения Kotlin, но имеют разные имена пакетов для Java. Поэтому переход на новые артефакты не потребует никаких изменений в вашем исходном коде.

Другое изменение, сделанное для обеспечения совместимости с новой системой модулей, — удаление устаревших объявлений в пакете kotlin.reflect из библиотеки kotlin-reflect. Если вы их использовали, вам необходимо перейти к использованию объявлений в пакете kotlin.reflect.full, который поддерживается с Kotlin 1.1.

windowed, chunked, zipWithNext

Новые расширения для Iterable<T>, Sequence<T>, и CharSequence охватывают такие варианты использования, как буферизация или пакетная обработка (chunked), скользящее окно и вычисление скользящего среднего (windowed), и обработка пар последовательных элементов (zipWithNext):

fun main(args: Array<String>) {
//sampleStart
    val items = (1..9).map { it * it }

    val chunkedIntoLists = items.chunked(4)
    val points3d = items.chunked(3) { (x, y, z) -> Triple(x, y, z) }
    val windowed = items.windowed(4)
    val slidingAverage = items.windowed(4) { it.average() }
    val pairwiseDifferences = items.zipWithNext { a, b -> b - a }
//sampleEnd

    println("items: $items\n")

    println("chunked into lists: $chunkedIntoLists")
    println("3D points: $points3d")
    println("windowed by 4: $windowed")
    println("sliding average by 4: $slidingAverage")
    println("pairwise differences: $pairwiseDifferences")
}

fill, replaceAll, shuffle/shuffled

Для обработки списков добавлена группа функций-расширений: fill, replaceAll и shuffle для MutableList, и shuffled для чтения-только List:

fun main(args: Array<String>) {
//sampleStart
    val items = (1..5).toMutableList()
    
    items.shuffle()
    println("Shuffled items: $items")
    
    items.replaceAll { it * 2 }
    println("Items doubled: $items")
    
    items.fill(5)
    println("Items filled with 5: $items")
//sampleEnd
}

Математические операции в kotlin-stdlib

В соответствии с давней просьбой, Kotlin 1.2 добавляет API kotlin.math для математических операций, который является общим для JVM и JS и содержит следующее:

  • Константы: PI и E

  • Тригонометрические: cos, sin, tan и обратные к ним: acos, asin, atan, atan2

  • Гиперболические: cosh, sinh, tanh и их обратные: acosh, asinh, atanh

  • Возведение в степень: pow (функция-расширение), sqrt, hypot, exp, expm1

  • Логарифмы: log, log2, log10, ln, ln1p

  • Округление:

    • ceil, floor, truncate, round (функции половина до четного)

    • roundToInt, roundToLong (функции половина до целого)

  • Знак и абсолютное значение:

    • abs и sign функции

    • absoluteValue и sign расширяющие свойства

    • withSign функция-расширение

  • max и min двух значений

  • Двоичное представление:

    • ulp расширяющее свойство

    • nextUp, nextDown, nextTowards функции-расширения

    • toBits, toRawBits, Double.fromBits (они находятся в пакете kotlin)

Тот же набор функций (но без констант) также доступен для Float аргументов.

Операторы и преобразования для BigInteger и BigDecimal

Kotlin 1.2 вводит набор функций для работы с BigInteger и BigDecimal и создания их из других числовых типов. Эти функции:

  • toBigInteger для Int и Long

  • toBigDecimal для Int, Long, Float, Double, и BigInteger

  • Функции арифметических и побитовых операторов:

    • Бинарные операторы +, -, *, /, % и инфиксные функции and, or, xor, shl, shr

    • Унарные операторы -, ++, --, и функция inv

Преобразования чисел с плавающей запятой в биты

Были добавлены новые функции для преобразования Double и Float в их битовое представление и обратно:

  • toBits и toRawBits возвращающие Long для Double и Int для Float

  • Double.fromBits и Float.fromBits для создания чисел с плавающей запятой из их битового представления

Regex теперь сериализуем

Класс kotlin.text.Regex стал Serializable и теперь может использоваться в иерархиях, поддерживающих сериализацию.

Closeable.use вызывает Throwable.addSuppressed, если доступно

Функция Closeable.use вызывает Throwable.addSuppressed, когда при закрытии ресурса после другой исключительной ситуации возникает исключение.

Для активации этого поведения необходимо иметь kotlin-stdlib-jdk7 в своих зависимостях.

JVM бэкенд

Нормализация вызовов конструкторов

С версии 1.0 Kotlin поддерживает выражения со сложной структурой управления, такие как выражения try-catch и вызовы встроенных функций. Такой код соответствует спецификации Java Virtual Machine. К сожалению, некоторые инструменты обработки байткода некорректно обрабатывают подобный код, когда такие выражения присутствуют в аргументах вызовов конструкторов.

Чтобы решить эту проблему для пользователей таких инструментов обработки байткода, мы добавили опцию компилятора командной строки (-Xnormalize-constructor-calls=MODE), которая сообщает компилятору сгенерировать более Java-подобный байткод для таких конструкций. Здесь MODE может быть:

  • disable (по умолчанию) – сгенерировать байткод таким же образом, как в Kotlin 1.0 и 1.1.

  • enable – сгенерировать Java-подобный байткод для вызовов конструкторов. Это может изменить порядок загрузки и инициализации классов.

  • preserve-class-initialization – сгенерировать Java-подобный байткод для вызовов конструкторов, гарантируя сохранение порядка инициализации классов. Это может повлиять на общую производительность приложения; используйте его только если у вас есть какая-то сложная общая структура данных, используемая между несколькими классами и обновляемая при инициализации класса.

«Ручной» обходной путь заключается в сохранении значений подвыражений с потоком управления в переменных вместо их непосредственного вычисления в аргументах вызова. Это похоже на -Xnormalize-constructor-calls=enable.

Вызовы Java-методов по умолчанию

До Kotlin 1.2, члены интерфейсов, переопределяющие Java-методы по умолчанию, в то же время нацеленные на JVM 1.6, приводили к предупреждению о вызовах super: Super calls to Java default methods are deprecated in JVM target 1.6. Recompile with '-jvm-target 1.8'. В Kotlin 1.2 используется ошибка вместо этого, поэтому любой такой код должен компилироваться с целевой JVM 1.8.

Изменение: согласованное поведение x.equals(null) для платформных типов

Вызов x.equals(null) для платформного типа, сопоставленного с примитивным Java-типом (Int!, Boolean!, Short!, Long!, Float!, Double!, Char!) неправильно возвращал true, когда x было null. Начиная с Kotlin 1.2, вызов x.equals(...) для значения null платформного типа вызывает исключение NPE (но x == ... не вызывает его).

Чтобы вернуться к поведению до версии 1.2, передайте флаг -Xno-exception-on-explicit-equals-for-boxed-null компилятору.

Изменение: исправление утечки null через встроенный расширяющий приемник для платформных типов

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

Чтобы вернуться к старому поведению, передайте флаг отката -Xno-receiver-assertions компилятору.

JavaScript бэкенд

Поддержка TypedArrays включена по умолчанию

Поддержка JS массивов с типом, которая преобразовывает массивы примитивных типов Kotlin, такие как IntArray, DoubleArray, в массивы с типом JavaScript, ранее являвшаяся опциональной, теперь включена по умолчанию.

Инструменты

Предупреждения как ошибки

Теперь компилятор предоставляет возможность обрабатывать все предупреждения как ошибки. Используйте -Werror в командной строке или следующий фрагмент Gradle:

compileKotlin {
    kotlinOptions.allWarningsAsErrors = true
}
Последнее изменение: 06 сентября 2022
Что нового в Kotlin 1.3 Что нового в Kotlin 1.1

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

Spec-Zone.ru

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