《Kotlin核心编程》 ——3.4.3 copy、componentN与解构
3.4.3 copy、componentN与解构
我们继续来看上面代码中的一段:
public final Bird copy(double weight, int age, @NotNull String color) {
Intrinsics.checkParameterIsNotNull(color, "color");
return new Bird (weight, age, color);
}
public static Bird copy$default(Bird var0, double var1, int var3, String var4, int var5, Object var6) { //var0代表被copy的对象
if ((var5 & 1) != 0) {
var1 = var0.weight; //copy时若未指定具体属性的值,则使用被copy对象的属性值
}
if ((var5 & 2) != 0) {
var3 = var0.age;
}
if ((var5 & 4) != 0) {
var4 = var0.color;
}
return var0.copy(var1, var3, var4);
}
这段代码中的copy方法的主要作用就是帮我们从已有的数据类对象中拷贝一个新的数据类对象。当然你可以传入相应参数来生成不同的对象。但同时我们发现,在copy的执行过程中,若你未指定具体属性的值,那么新生成的对象的属性值将使用被copy对象的属性值,这便是我们平常所说的浅拷贝。我们来看下面这个例子:
Bird b1 = new Bird(20.0,1,"blue");
Bird b2 = b1;
b2.setColor("red");
System.out.println(b1.getColor()); //red
类似这样的代码很多人都写过,但这种方式会带来一个问题,明明是对一个新的对象b2做了修改,为什么还会影响老的对象b1呢?其实这只是一种表象而已。实际上,除了基本数据类型的属性,其他属性还是引用同一个对象,这便是浅拷贝的特点。
实际上copy更像是一种语法糖,假如我们的类是不可变的,属性不可以修改,那么我们只能通过copy来帮我们基于原有对象生成一个新的对象。比如下面的两个例子:
//声明的Bird属性可变
val b1 = Bird(20.0, 1, "blue")
val b2 = b1
b2.age = 2
//声明的Bird属性不可变
val b1 = Bird(20.0, 1, "blue")
val b2 = b1.copy(age = 2) //只能通过copy
copy更像提供了一种简洁的方式帮我们复制一个对象,但它是一种浅拷贝的方式。所以在使用copy的时候要注意使用场景,因为数据类的属性可以被修饰为var,这便不能保证不会出现引用修改问题。
接下来我们来看看componentN方法。简单来说,componentN可以理解为类属性的值,其中N代表属性的顺序,比如component1代表第1个属性的值,component3代表第3个属性的值。那么,这样设计到底有什么用呢?我们来思考一个问题,我们或多或少地知道怎么将属性绑定到类上,但是对如何将类的属性绑定到相应变量上却不是很熟悉。比如:
val b1 = Bird(20.0, 1, "blue")
//通常方式
val weight = b1.weight
val age = b1.age
val color = b1.color
//kotlin进阶
val (weight, age, color) = b1
看到Kotlin的语法相信你一定会感到兴奋,因为你可能写过类似下面的代码:
String birdInfo = "20.0,1,bule";
String[] temps = birdInfo.split(",");
double weight = Double.valueOf(temps[0]);
int age = Integer.valueOf(temps[1]);
String color = temps[2];
这样代码有时真的很烦琐,我们明明知道值的情况,却要分好几步来给变量赋值。很幸运,Kotlin提供了更优雅的做法:
val (weight, age, color) = birdInfo.split(",");
这个语法很简洁也很直观。那么这到底是一种什么魔法呢?其实原理也很简单,就是解构,通过编译器的约定实现解构。
当然Kotlin对于数组的解构也有一定限制,在数组中它默认最多允许赋值5个变量,因为若是变量过多,效果反而会适得其反,因为到后期你都搞不清楚哪个值要赋给哪个变量了。所以一定要合理使用这一特性。
在数据类中,你除了可以利用编译器帮你自动生成componentN方法以外,甚至还可以自己实现对应属性的componentN方法。比如:
data class Bird(var weight: Double, var age: Int, var color: String) {
var sex = 1
operator fun component4(): Int { //operator关键字
return this.sex
}
constructor(weight: Double, age: Int, color: String, sex: Int) : this(weight, age, color) {
this.sex = sex
}
}
fun main(args: Array<String>) {
val b1 = Bird(20.0, 1, "blue", 0)
val (weight, age, color, sex) = b1
…
}
除了数组支持解构外,Kotlin也提供了其他常用的数据类,让使用者不必主动声明这些数据类,它们分别是Pair和Triple。其中Pair是二元组,可以理解为这个数据类中有两个属性;Triple是三元组,对应的则是3个属性。我们先来看一下它们的源码:
//Pair
public data class Pair<out A, out B>(
public val first: A,
public val second: B)
//Triple
public data class Triple<out A, out B, out C>(
public val first: A,
public val second: B,
public val third: C)
可以发现Pair和Triple都是数据类,它们的属性可以是任意类型,我们可以按照属性的顺序来获取对应属性的值。因此,我们可以这么使用它们:
val pair = Pair(20.0, 1)
val triple = Triple(20.0, 1, "blue")
//利用属性顺序获取值
val weightP = pair.first
val ageP = pair.second
val weightT = triple.first
val ageT = triple.second
val colorT = triple.third
//当然我们也可以利用解构
val (weightP, ageP) = Pair(20.0, 1)
val (weightT, ageT, colorT) = Triple(20.0, 1, "blue")
数据类中的解构基于componentN函数,如果自己不声明componentN函数,那么就会默认根据主构造函数参数来生成具体个数的componentN函数,与从构造函数中的参数无关。
- 点赞
- 收藏
- 关注作者
评论(0)