开发中有时只是想分解一个包含多个字段的对象来初始化几个单独的变量。要实现这一点,可以使用Kotlin的解构声明。
本文主要了解:
“1、如何使用解构声明这种特性
2、底层是如何实现的
3、如何在你自己的类中实现它
1、解构声明的使用
解构声明(Destructuring Declarations
)概念:将对象所包含的字段视为一组独立的变量
解构声明允许我们这样定义局部变量
:
//Perosn对象定义
data class Person(var id: Int, var name: String, var age: Int)
fun main() {
val person = Person(1, "张三", 20)
//解构声明,id/name/age新定义的三个变量分别对应Person中的三个字段
val(id, name, age) = person
//访问解构声明的name字段
println("这个人的名字: $name")
}
在处理返回值
时,也可以使用解构声明:
fun getPersonInfo() = Person(2, "李四", 35)
val(id, name, age) = getPersonInfo()
或者如果我们需要从一个函数中返回两个值
:
fun getTwoValues(): Pair<Int, String> {
// ...
return Pair(1, "success")
}
val (result, status) = getTowValues()
使用for循环迭代集合
可以通过解构声明完成,如下所示:
for ((a, b) in collection) { ... }
上面代码中变量a和b对应返回集合中的前两个元素。
然而,在Map
中,如下面代码中,变量a和b将分别对应的是key和value:
var map: HashMap<Int, Person> = HashMap()
map.put(1, person)
for((key, value) in map){
println("Key: $key, Value: $value")
}
默认情况下,所有数据类data calss
都支持解构。
你可以决定只使用类字段中的一个变量子集
:
//Perosn对象定义
data class Person(var id: Int, var name: String, var age: Int)
fun main() {
val person = Person(1, "张三", 20)
//val(id, name, age) = person
//结构Person类中前两个字段
val(id, name) = person
}
解构不允许你准确地选择你想要使用的字段;它总是使用前n
个字段,其中n
是您声明的变量的数量。这样做的缺点是很容易犯错误。例如,下面的代码片段可能会提供一个意想不到的结果:
如果你只需要一个非连续字段的子集,对那些你不感兴趣的字段使用_,Kotlin会跳过它们。上面的例子变成:
fun main() {
val person = Person(1, "张三", 20)
val (_,name,age) = person
println(name)
println(age)
}
2、底层实现:
让我们看看一个反编译的数据类,看看发生了什么。我们将只关注为解构而生成的函数。
要查看Java反编译代码,请转到Tools -> Kotlin -> Show Kotlin Bytecode,然后按下Decompile按钮。
public final class Person {
private int id;
@NotNull
private String name;
private int age;
//...
public Person(int id, @NotNull String name, int age) {
//...
}
public final int component1() {
return this.id;
}
@NotNull
public final String component2() {
return this.name;
}
public final int component3() {
return this.age;
}
}
我们看到,在主构造函数中声明的每个属性都会生成名为componentN的函数,其中N
是主构造函数中字段的索引
。
3、实现解构
正如我们所看到的,解构依赖于componentN函数。因此,如果您想将解构功能添加到不支持它的类中,那么只需实现相应的componentN操作符函数即可。确保在它们前面加上operator关键字。
class Student(
val name: String,
val age: String
) {
operator fun component1(): String = name
operator fun component2(): Int = age
...
}
Kotlin允许您通过扩展函数为不属于自己的类实现解构。例如,Map.Entry只是一个接口,默认情况下它不支持解构。为了克服这个问题,创建了component1()和component2()函数,它们返回Map.Entry的键和值。
具体可以看Map.Entry源码或者API文档:https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-map/-entry/
总结
当您需要将对象的字段分解为变量时,请使用解构。在底层,解构是通过提供componentN操作符函数实现的,因此您可以自己为您认为从该功能中受益的类提供这些操作符函数。