Spec-Zone.ru › Kotlin 2

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

Выпущен: 28 ноября 2017 г.

Содержание

  • Мультиплатформенные проекты

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

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

  • Бэкенд JVM

  • Бэкенд JavaScript

Информацию о цикле выпуска Kotlin см. в разделе Процесс выпуска Kotlin.

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

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

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

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

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

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

Ключевая возможность поддержки мультиплатформенных проектов — возможность выражать зависимости общего кода от платформенных частей с помощью ожидаемых и фактических объявлений. Ожидаемое объявление задает API (класс, интерфейс, аннотацию, объявление верхнего уровня и т. д.). Фактическое объявление — это либо платформозависимая реализация 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()
}

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

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

//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 станет ошибкой.

Устаревшая возможность: вложенные типы в элементах enum

Определение внутри элементов enum вложенного типа, который не является 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, могли нарушать безопасность типов при обработке исключений с помощью 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 и вызовы встроенных функций. Такой код допустим согласно спецификации виртуальной машины Java. К сожалению, некоторые инструменты обработки байт-кода работают с таким кодом некорректно, если подобные выражения находятся в аргументах вызовов конструкторов.

Чтобы смягчить эту проблему для пользователей таких инструментов обработки байт-кода, мы добавили параметр компилятора командной строки (-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
}
15 января 2026 г.
Руководство по совместимости для Kotlin 1.3.xЧто нового в Kotlin 1.1

© 2010–2026 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