解构声明
有些时候, 能够将一个对象 解构(destructure) 为多个变量, 将会很方便, 比如:
这种语法称为 解构声明(destructuring declaration). 一个解构声明会一次性创建多个变量. 上例中你声明了两个变量: name 和 age, 并且可以独立地使用这两个变量:
解构声明在编译时将被分解为以下代码:
这里的 component1() 和 component2() 函数是 Kotlin 中广泛使用的 约定原则(principle of convention) 的又一个例子 (其它例子请参见 + 和 * 操作符, for 循环). 任何东西都可以作为解构声明右侧的被解构值, 只要可以对它调用足够数量的组件函数(component function). 当然, 还可以存在 component3() 和 component4() 等等.
解构声明还可以使用在 for 循环中:
上面的代码将遍历集合中的所有元素, 然后对各个元素调用 component1() 和 component2() 函数, 变量 a 和 b 将得到 component1() 和 component2() 函数的返回值.
示例: 从一个函数返回两个值
假如你需要从一个函数返回两个值, 比如, 一个是结果对象, 另一个是某种状态值. 在 Kotlin 中有一种紧凑的方法实现这个功能, 我们可以声明一个 数据类, 然后返回这个数据类的一个实例:
由于数据类会自动声明 componentN() 函数, 因此可以在这里使用解构声明.
示例: 解构声明与 Map
遍历一个 map 的最好的方式可能就是:
为了让上面的代码正确运行, 你应该:
实现
iterator()函数, 使得 map 成为多个值构成的序列.实现
component1()和component2()函数, 使得 map 内的每个元素成为一对值.
Kotlin 的标准库也的确实现了这些扩展函数:
因此, 你可以在对 map 的 for 循环中自由地使用解构声明 (也可以在对数据类集合的 for 循环中使用解构声明).
用下划线代替未使用的变量
如果在解构声明中, 你不需要其中的某个变量, 你可以用下划线来代替变量名:
以这种方式跳过的变量, 不会调用对应的 componentN() 操作符函数.
在 Lambda 表达式中使用解构声明
你可以在 lambda 表达式的参数中使用解构声明语法. 如果 lambda 表达式的一个参数是 Pair 类型 (或 Map.Entry 类型, 或者任何其他类型, 只要它拥有适当的 componentN 函数), 就可以使用几个新的参数来代替原来的参数, 只需要将新参数包含在括号内:
请注意声明两个参数, 与将一个参数解构为多个参数的区别:
如果解构后得到的某个参数未被使用到, 你可以用下划线代替它, 这样就不必为它编造一个变量名了:
你可以为解构前的整个参数指定类型, 也可以为解构后的部分参数单独指定类型:
基于名称的解构
Kotlin 支持 基于名称的解构声明, 变量按照名称与属性匹配, 而不是象 基于位置 的解构那样, 按照 componentN() 函数定义的位置进行匹配.
在基于位置的解构中, 变量与 componentN() 函数的顺序相对应, 例如:
在这个示例中, 由于解构依赖于 componentN() 函数的顺序, email 会得到 username 的值, username 会得到 email 的值.
使用基于名称的解构, 会根据属性名决定获取哪个值, 而不是由 componentN() 函数的位置决定:
基于名称的解构是 实验性功能. 启用这个功能时, 它还会引入基于位置的解构的新的语法, 使用方括号. 对于元素顺序至关重要的类型, 例如 List 和其它有顺序的集合, 以及没有名称的元组, 例如 Pair 或 Triple, 请使用这个语法:
你可以使用 -Xname-based-destructuring 编译器选项, 控制编译器如何解释解构声明.
这个选项包含以下模式:
only-syntax: 启用基于名称的解构的明确调用形式, 不改变既有的解构声明的行为.name-mismatch: 当对数据类使用基于位置的解构时, 如果使用的变量名称与属性名称不匹配, 报告警告.complete: 启用基于名称的解构的圆括号简写形式, 并且通过方括号语法, 继续支持基于位置的解构.
如果你使用 complete 模式, 解构语法的圆括号简写形式会将变量匹配到属性名称, 而不是依赖它的位置:
要在你的项目中启用基于名称的解构, 请对你的构建配置文件添加编译器选项: