Kotlin支持一种非常方便的方法读取一个对象的属性。类似这种写法:
val (name, age) = person
如上,我们可以方便的获取person对象中的属性。
当然,这样的特性不是天生就有的,我们可能需要一些额外的工作。
跟着这边文章,我们来看一下,如何编写Kotlin的解构代码。
假设我们创建一个Pair对象:
val pair = Pair("Bob", 12)
备注,创建Pair有一个更加优雅的写法:
val pair = "Bob" to 12
Great!
接着,我们想获取对象pair中的两个值:
val pair = "Bob" to 12
val name = pair.first
val age = pair.second
非常正确。
不过借助于Kotlin的解构,我们还能写的更加简洁:
val pair = "Bob" to 12
val (name, age) = pair
解构声明时,我们使用一对括号,放入我们的变量,然后直接=一个对象,就是一段解构声明。Kotlin会自动按照类变量声明的顺序,将相关的值取出赋值给=左边的变量。例如这里的Pair类,我们在创建的时候,先声明了Bob,然后声明了12,这样在解构时,第一个变量就会给予值Bob,第二个变量就会给予12这个值。
除了Pair类,Kotlin的集合也支持解构声明:
val alpha = listOf("A", "B", "C")
val (a, b, c) = alpha
// a = "A" b = "B" c = "C"
此时,变量a,b,c均赋值了列表中正确的值。这里有个小问题,如果我们变量的数量多于列表的大小呢?
例如:
val (a, b, c, d) = alpha
在执行到这段代码的时候,我们会得到一个异常:
java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
所以,在使用列表解构的时候,要非常小心列表的大小问题,不然会直接收获一个异常。
同样,Map类型也支持解构:
val persons = mapOf(
"Bob" to 12,
"Josh" to 14,
"Linda" to 13
)
for ((name, age) in persons) {
println("$name is $age year(s) old")
}
输出为:
Bob is 12 year(s) old
Josh is 14 year(s) old
Linda is 13 year(s) old
在循环时,我们使用for ((name, age) in persons)的写法,让Kotlin自动帮我们执行Map的遍历与键值对的解构,循环中,我们就可以直接使用name和age了。
Kotlin的data class,为我们自动提供了解构的特性。
fun main() {
val linda = Person("Linda", 13)
val (name, age) = linda
}
data class Person(val name: String, val age: Int)
我们声明了一个Person类,该类是data class,提供两个属性name与age,我们可以直接对该类的对象使用解构声明val (name, age) = linda,此时,我们能够得到正确的值。
但是关于data class的解构有一个小陷阱:Kotlin在支持data class解构时,是按照变量声明顺序支持的,也就是说,data class的变量声明顺序,直接影响到了解构的结果。
例如:
fun main() {
val linda = Person("Linda", 6, 13)
val (name, age) = linda
println(age)
}
data class Person(val name: String, val grade: Int, val age: Int)
上述代码我们稍作修改,在age前面添加一个属性grade,而我们的解构声明并没有做相应的改变,此时,变量age所对应的值,已经不再是Person.age了,而是Person.grade,所以大家在data class中使用解构时需要额外注意。有两种方法可以防止这样的错误情况发生:
- 从不使用
data class的解构声明。 data class在新添加属性的时候,一定要在后添加,而不能在中间或前面。
Kotlin自动提供的解构声明,大概就是如上几种情况了,那么如果我们自己声明一个普通类,如何才能支持解构呢?
class Student(val name: String, val age: Int)
我们创建了这样一个类,想支持解构。
其实非常简单,Kotlin为我们提供了相关的操作符重载函数componentN()function(N >= 1):
class Student(val name: String, val age: Int) {
operator fun component1(): String {
return name
}
operator fun component2(): Int {
return age
}
}
添加两个操作符重载函数,即可实现普通类的解构:
val linda = Student("Linda", 13)
val (name, age) = linda
It works!
当然,普通类的解构,完全取决于我们的componentN()function返回什么。所以,在使用普通类解构时,我们也有一个要求防止后续代码更改带来的问题:
- 已经有的解构声明,不做改动。
以
Student举例,后续如果我们需要为新的属性提供解构的话,那么就添加新的component3(),component4(),现有存在的componentN()function就不要做任何改动了。
OK,关于Kotlin的解构技巧就讲到这里,希望对大家有所帮助。