Что нового в Kotlin 1.2
Выпущено: 28 ноября 2017 года
Содержание
Многоплатформенные проекты (экспериментально)
Многоплатформенные проекты — новая экспериментальная возможность в Kotlin 1.2, позволяющая повторно использовать код между целевыми платформами, поддерживаемыми Kotlin — JVM, JavaScript и (в будущем) Native. В многоплатформенном проекте у вас есть три вида модулей:
Модуль common содержит код, не специфичный для какой-либо платформы, а также объявления без реализации платформенно-зависимых API.
Модуль platform содержит реализации платформенно-зависимых объявлений из модуля common для конкретной платформы, а также другой платформенно-зависимый код.
Обычный модуль нацелен на конкретную платформу и может быть зависимостью от модулей platform или зависеть от них.
При компиляции многоплатформенного проекта для конкретной платформы генерируется код как для общих, так и для платформенно-специфичных частей.
Ключевой особенностью поддержки многоплатформенных проектов является возможность выражать зависимости кода common от платформенно-специфических частей через expected и actual объявления. Expected объявление определяет API (класс, интерфейс, аннотация, объявление верхнего уровня и т.д.). Actual объявление — это либо платформенно-зависимая реализация API, либо псевдоним типа, ссылающийся на существующую реализацию API в внешней библиотеке. Вот пример:
В коде common:
// 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 platform:
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 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, устарело с предупреждением в 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.
Стандартная библиотека
Артефакты стандартной библиотеки 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иLongtoBigDecimalдляInt,Long,Float,Double, иBigInteger-
Арифметические и побитовые операторные функции:
Бинарные операторы
+,-,*,/,%и инфиксные функцииand,or,xor,shl,shrУнарные операторы
-,++,--, и функцияinv
Преобразования чисел с плавающей точкой в биты
Были добавлены новые функции для преобразования Double и Float в их битовое представление и наоборот:
toBitsиtoRawBitsвозвращающиеLongдляDoubleиIntдляFloatDouble.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 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
}
© 2010–2023 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/whatsnew12.html