Spec-Zone.ru › Kotlin 1.6

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

Дата выпуска: 28 ноября 2017 года

Содержание

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

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

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

  • JVM-бекенд

  • JavaScript-бекенд

Многоплатформенные проекты (экспериментальная функция)

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

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

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

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

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

Ключевой особенностью поддержки многоплатформенных проектов является возможность выражать зависимости общего кода от платформенно-специфичных частей через объявления 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 {
    // ...
}

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

Свойства и локальные переменные top-level lateinit

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

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 var

Теперь вы можете проверить, была ли инициализирована lateinit var, используя 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()
}

Встроенные функции с параметрами функций по умолчанию

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

//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 в API Android уровня 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.

Устаревание: переопределение copy в классах данных

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

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

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

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

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

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

foo(items = *arrayOf(1))

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

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

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

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

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

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

Артефакты стандартной библиотеки 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 и вызовы inline-функций. Такой код соответствует спецификации 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 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 typed arrays, которая преобразует Kotlin-примитивные массивы, такие как IntArray, DoubleArray, в JavaScript typed arrays, которая ранее была опциональной, теперь включена по умолчанию.

Инструменты

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

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

compileKotlin {
    kotlinOptions.allWarningsAsErrors = true
}
Последнее изменение: 07 апреля 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