Spec-Zone.ru › Kotlin 1.8

Распаковывающие объявления

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

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!" }
Последнее изменение: 10 января 2023
Аннотации Рефлексия

© 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