Kotlin Reference: Destructuring Declarations

  有时候需要把一个对象解构(Destructure)为一系列变量,如:

[code lang=”kotlin”]val (name, age) = person[/code]

上面的语法称为解构声明(Destructuring Declaration),解构声明可以一次性创建多个变量。我们声明了两个变量:nameage,可以单独地使用它们:

[code lang=”kotlin”]println(name)
println(age)[/code]

  解构声明会编译为如下的代码:

[code lang=”kotlin”]val name = person.component1()
val age = person.component2()[/code]

这里的 component1()component2() 函数是在 Kotlin 中广泛使用另一个的约定规则(其他约定如 +* 操作符、for 循环等)。只要对象提供了必要数量的 component 函数,该对象就可以用于解构声明(放在解构声明的右边)。当然,也可以存在 component3()component4() 等方法。

  注意必须使用 operator 关键词标记 componentN() 函数,这样该函数才能用于解构声明。

  解构声明也可以用于 for 循环,当使用:

[code lang=”kotlin”]for ((a, b) in collection) { … }[/code]

变量 ab 的值是在集合元素上调用 component1()component2() 的返回值。

Example: Returning Two Values from a Function

  假设我们想从一个函数中返回两个值,比如一个结果对象和一个状态值,在 Kotlin 中可以声明一个数据类并返回其实例,十分简洁:

[code lang=”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(…)[/code]

  由于数据类会自动声明 componentN() 函数,故可以使用解构声明。

  注意:虽然我们也可以使用标准类 Pair 并让 function() 返回 Pair<Int, Status>,但对数据进行合适的命名往往会更好。

Example: Destructuring Declarations and Maps

  遍历一个映射的最好方法可能是:

[code lang=”kotlin”]for ((key, value) in map) {
// do something with the key and the value
}[/code]

  要让上面的代码能够工作,我们应当:

  • 通过提供 iterator() 函数,把 map 表示为一连串的值,
  • 通过提供 component1()component2() 函数,把映射中的元素表示为成对的形式。

  实际上,标准库提供了这些扩展:

[code lang=”kotlin”]operator fun Map.iterator(): Iterator> = entrySet().iterator()
operator fun Map.Entry.component1() = getKey()
operator fun Map.Entry.component2() = getValue()[/code]

所以我们可以对 for 循环中的 map 进行任意的解构(也适用于数据类实例的集合等)。

Underscore for unused variables (since 1.1)

  如果不需要某个解构声明中的变量,可以使用下划线来代替名称。

[code lang=”kotlin”]val (_, status) = getResult()[/code]

Destructuring in Lambdas (since 1.1)

  解构声明的语法可以用于 Lambda 的参数。如果 Lambda 的某个参数是 Pair 型(或 Map.Entry,或者其他有适当 componentN 函数的类型)的,则该参数就可以变为放在括号中的多个参数:

[code lang=”kotlin”]map.mapValues { entry -> “${entry.value}!” }
map.mapValues { (key, value) -> “$value!” }[/code]

  注意区分声明两个参数和声明一个参数的解构对的区别:

[code lang=”kotlin”]{ a -> … } // one parameter
{ a, b -> … } // two parameters
{ (a, b) -> … } // a destructured pair
{ (a, b), c -> … } // a destructured pair and another parameter[/code]

  如果没有用到解构参数的某个成分,则可以使用下划线替代,避免为其命名:

[code lang=”kotlin”]map.mapValues { (_, value) -> “$value!” }[/code]

  你可以指定整个解构的参数的类型,也可以单独指定其某一个成分的类型:

[code lang=”kotlin”]map.mapValues { (_, value): Map.Entry -> “$value!” }

map.mapValues { (_, value: String) -> “$value!” }[/code]