Spec-Zone.ru › Kotlin 1.9

Деструктурирующие объявления

Иногда удобно деструктурировать объект на несколько переменных, например:

val (name, age) = person 

Этот синтаксис называется деструктурирующим объявлением. Деструктурирующее объявление создаёт сразу несколько переменных. Вы объявили две новые переменные: name и age, и можете использовать их независимо:

println(name)
println(age)

Деструктурирующее объявление компилируется в следующий код:

val name = person.component1()
val age = person.component2()

Функции component1() и component2() — это ещё один пример принципа соглашений, широко используемого в Kotlin (см. операторы, такие как + и *, циклы for в качестве примера). Правой частью деструктурирующего объявления может быть что угодно, при условии, что на нём можно вызвать необходимое количество компонентных функций. И, конечно, может быть component3() и component4() и так далее.

Функции componentN() нужно пометить ключевым словом operator, чтобы разрешить их использование в деструктурирующем объявлении.

Деструктурирующие объявления также работают в циклах for:

for ((a, b) in collection) { ... }

Переменные a и b получают значения, возвращаемые функциями component1() и component2(), вызываемыми на элементах коллекции.

Пример: возвращение двух значений из функции

Предположим, что вам нужно вернуть две вещи из функции — например, объект результата и статус какого-то типа. Компактный способ сделать это в Kotlin — объявить данный класс и вернуть его экземпляр:

data class Result(val result: Int, val status: Status)
fun function(...): Result {
    // computations
    
    return Result(result, status)
}

// Now, to use this function:
val (result, status) = function(...)

Поскольку данные классы автоматически объявляют функции componentN(), деструктурирующие объявления здесь работают.

Можно также использовать стандартный класс Pair и заставить function() возвращать Pair<Int, Status>, но часто лучше иметь ваши данные с правильными именами.

Пример: деструктурирующие объявления и карты

Вероятно, лучший способ пройтись по карте — это:

for ((key, value) in map) {
   // do something with the key and the value
}

Для того, чтобы это работало, вы должны:

  • Представить карту как последовательность значений, предоставив функцию iterator().

  • Представить каждый из элементов как пару, предоставив функции component1() и component2().

И действительно, стандартная библиотека предоставляет такие расширения:

operator fun <K, V> Map<K, V>.iterator(): Iterator<Map.Entry<K, V>> = entrySet().iterator()
operator fun <K, V> Map.Entry<K, V>.component1() = getKey()
operator fun <K, V> Map.Entry<K, V>.component2() = getValue()

Таким образом, вы можете свободно использовать деструктурирующие объявления в циклах for с картами (а также коллекциями экземпляров данных классов или аналогичными).

Подчёркивание для неиспользуемых переменных

Если вам не нужна переменная в деструктурирующем объявлении, вы можете поместить подчёркивание вместо её имени:

val (_, status) = getResult()

Функции операторов componentN() не вызываются для компонентов, которые пропускаются таким образом.

Деструктурирование в лямбда-выражениях

Вы можете использовать синтаксис деструктурирующих объявлений для параметров лямбда-выражений. Если лямбда имеет параметр типа Pair (или Map.Entry, или любой другой тип, который имеет соответствующие функции componentN), вы можете ввести несколько новых параметров вместо одного, поместив их в скобки:

map.mapValues { entry -> "${entry.value}!" }
map.mapValues { (key, value) -> "$value!" }

Обратите внимание на разницу между объявлением двух параметров и объявлением деструктурированной пары вместо параметра:

{ a -> ... } // one parameter
{ a, b -> ... } // two parameters
{ (a, b) -> ... } // a destructured pair
{ (a, b), c -> ... } // a destructured pair and another parameter

Если компонент деструктурированного параметра не используется, вы можете заменить его подчёркиванием, чтобы избежать изобретения его имени:

map.mapValues { (_, value) -> "$value!" }

Вы можете указать тип для всего деструктурированного параметра или для конкретного компонента отдельно:

map.mapValues { (_, value): Map.Entry<Int, String> -> "$value!" }

map.mapValues { (_, value: String) -> "$value!" }
Последнее изменение: 11 февраля 2021 г.
Аннотации Рефлексия

© 2010–2023 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/destructuring-declarations.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API