Что нового в Kotlin 1.2
Дата выпуска: 28 ноября 2017 года
Содержание
Проекты кроссплатформенной разработки (экспериментальная функция)
Проекты кроссплатформенной разработки — новая экспериментальная функция в Kotlin 1.2, позволяющая повторно использовать код между целевыми платформами, поддерживаемыми Kotlin: JVM, JavaScript и (в будущем) Native. В проекте кроссплатформенной разработки есть три типа модулей:
Модуль common содержит код, не специфичный для какой-либо платформы, а также объявления без реализации платформенно-зависимых API.
Модуль platform содержит реализации платформенно-зависимых объявлений из модуля common для конкретной платформы, а также другой платформенно-зависимый код.
Обычный модуль предназначен для конкретной платформы и может быть зависимостью от модулей platform или зависеть от них.
При компиляции проекта кроссплатформенной разработки для определённой платформы генерируется код как для общих, так и для платформенно-специфичных частей.
Ключевой особенностью поддержки проектов кроссплатформенной разработки является возможность выражать зависимости общего кода от платформенно-специфичных частей через объявления 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:
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 в 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.
Устаревание: переопределение 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и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(...) для нулевого значения платформного типа выбрасывает исключение NPE (но x == ... не вызывает его).
Чтобы вернуться к поведению до версии 1.2, передайте флаг -Xno-exception-on-explicit-equals-for-boxed-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