Что нового в Kotlin 1.2
Дата выхода: 28 ноября 2017 года
Содержание
Проекты кросс-платформ (экспериментальная возможность)
Проекты кросс-платформ — новая экспериментальная возможность 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.
Стандартная библиотека
Артефакты стандартной библиотеки 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: 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
}
© 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