原文译文操作

Sometimes it is convenient to destructure an object into a number of variables, for example:

val (name, age) = person 

This syntax is called a destructuring declaration. A destructuring declaration creates multiple variables at once. We have declared two new variables: name and age, and can use them independently:

println(name)
println(age)

A destructuring declaration is compiled down to the following code:

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

The component1() and component2() functions are another example of the principle of conventions widely used in Kotlin (see operators like + and *for-loops etc.). Anything can be on the right-hand side of a destructuring declaration, as long as the required number of component functions can be called on it. And, of course, there can be component3() and component4() and so on.

Note that the componentN() functions need to be marked with the operator keyword to allow using them in a destructuring declaration.

Destructuring declarations also work in for-loops: when you say

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

Variables a and b get the values returned by component1() and component2() called on elements of the collection.

Example: Returning Two Values from a Function

Let's say we need to return two things from a function. For example, a result object and a status of some sort. A compact way of doing this in Kotlin is to declare a data class and return its instance:

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(...)

Since data classes automatically declare componentN() functions, destructuring declarations work here.

有时把一个对象 解构 成很多变量会很方便,例如:

val (name, age) = person

这种语法称为 解构声明 。一个解构声明同时创建多个变量。 我们已经声明了两个新变量:name 和 age,并且可以独立使用它们:

println(name)
println(age)

一个解构声明会被编译成以下代码:

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

其中的 component1() 和 component2() 函数是在 Kotlin 中广泛使用的 约定原则 的另一个例子。 (参见像 +和 *for-循环等操作符)。 任何表达式都可以出现在解构声明的右侧,只要可以对它调用所需数量的 component 函数即可。 当然,可以有 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 {
    // 各种计算

    return Result(result, status)
}

// 现在,使用该函数:
val (result, status) = function(……)

因为数据类自动声明 componentN() 函数,所以这里可以用解构声明。

纠正翻译

NOTE: we could also use the standard class Pair and have function() return Pair<Int, Status>, but it's often better to have your data named properly.

Example: Destructuring Declarations and Maps

Probably the nicest way to traverse a map is this:

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

To make this work, we should

  • present the map as a sequence of values by providing an iterator() function,
  • present each of the elements as a pair by providing functions component1() and component2().

And indeed, the standard library provides such extensions:

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()

So you can freely use destructuring declarations in for-loops with maps (as well as collections of data class instances etc).

Underscore for unused variables (since 1.1)

If you don't need a variable in the destructuring declaration, you can place an underscore instead of its name:

val (_, status) = getResult()

Destructuring in Lambdas (since 1.1)

You can use the destructuring declarations syntax for lambda parameters. If a lambda has a parameter of the Pair type (or Map.Entry, or any other type that has the appropriate componentN functions), you can introduce several new parameters instead of one by putting them in parentheses:

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

Note the difference between declaring two parameters and declaring a destructuring pair instead of a parameter:

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

If a component of the destructured parameter is unused, you can replace it with the underscore to avoid inventing its name:

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

You can specify the type for the whole destructured parameter or for a specific component separately:

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

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

注意:我们也可以使用标准类 Pair 并且让 function() 返回 Pair<Int, Status>, 但是让数据合理命名通常更好。

例:解构声明和映射

可能遍历一个映射(map)最好的方式就是这样:

for ((key, value) in map) {
   // 使用该 key、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-循环中对映射(以及数据类实例的集合等)自由使用解构声明。

下划线用于未使用的变量(自 1.1 起)

如果在解构声明中你不需要某个变量,那么可以用下划线取代其名称:

val (_, status) = getResult()

在 lambda 表达式中解构(自 1.1 起)

你可以对 lambda 表达式参数使用解构声明语法。 如果 lambda 表达式具有 Pair 类型(或者 Map.Entry 或任何其他具有相应 componentN 函数的类型)的参数,那么可以通过将它们放在括号中来引入多个新参数来取代单个新参数:

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

注意声明两个参数和声明一个解构对来取代单个参数之间的区别:

{ a //-> …… } // 一个参数
{ a, b //-> …… } // 两个参数
{ (a, b) //-> …… } // 一个解构对
{ (a, b), c //-> …… } // 一个解构对以及其他参数

如果解构的参数中的一个组件未使用,那么可以将其替换为下划线,以避免编造其名称:

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

你可以指定整个解构的参数的类型或者分别指定特定组件的类型:

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

map.mapValues { (_, value: String) -> "$value!" }
纠正翻译