问题
在使用数据类copy函数时,copy函数可以接收这个类的各种参数组合,编译器怎么实现的?它会为每种情况都会生成一个单独的copy函数吗?
源码
data class U(val p1: String,val p2:String,val p3:String,val p4:String)
fun main() {
val u = U("p1","p2","p3","p4")
val copy = u.copy()
}
反编译
//删除了部分代码
public final class T2Kt {
public static final void main() {
U u = new U("p1", "p2", "p3", "p4");
U copy = U.copy$default(user, (String)null, (String)null, (String)null, (String)null, 15, (Object)null);
}
}
public final class U {
public final U copy(@NotNull String p1, @NotNull String p2, @NotNull String p3, @NotNull String p4) {
return new U(p1, p2, p3, p4);
}
public static U copy$default(U var0, String var1, String var2, String var3, String var4, int var5, Object var6) {
if ((var5 & 1) != 0) {
var1 = var0.p1;
}
if ((var5 & 2) != 0) {
var2 = var0.p2;
}
if ((var5 & 4) != 0) {
var3 = var0.p3;
}
if ((var5 & 8) != 0) {
var4 = var0.p4;
}
return var0.copy(var1, var2, var3, var4);
}
}
当使用copy无参方法时,编译器帮我们自动生成一个var5=15,15的二进制为1111,在copy$default()方法中和1(0001),2(0010),4(0100),8(1000) 与操作结果全部不为0,所以每个参数值都会使用原对象的值,即没有参数值发生变化。
那只输入p1:u.copy(p1="c1"),编译器给我们生成的数字是多少呢?
14(1110),和1与操作后为0,var1就不会被原来对象中的p1覆盖,而使用我们传入的参数值。和2,4,8与操作全不为0,参数使用原来对象中的值。
那输入p1,p2呢?:u.copy(p1="c1",p2="c2"),编译器给我们生成的数字是多少呢?
12(1100),与1,2与操作后为0,所以结合代码来看就是p1,p2使用我们传入的参数值,p3,p4对应值被原对象覆盖。
总结
所以,从上面几个例子就可以得出规律:
4个参数可以用4个二进制位来表示,(0,0,0,0)->(p4,p3,p2,p1)。0代表输入了该位置参数,1代表未输入参数使用原对象值。只是二进制位顺序和参数顺序是反的,第1个二进制位代表了p4,最后一个二进制位代表p1。
例子:
- 1111->():没有输入
- 1011->(p3) :只输入p3
- 0111->(p4) :只输入p4
- 0011->(p3,p4) :输入p3,p4
U类一共有4个参数,那输入参数的情况就有2^4=16种。下面是所有情况:
| 输入参数 | 状态值 | 二进制 |
|---|---|---|
| 无 | 15 | 1111 |
| p1 | 14 | 1110 |
| p2 | 13 | 1101 |
| p1,p2 | 12 | 1100 |
| p3 | 11 | 1011 |
| p1,p3 | 10 | 1010 |
| p2,p3 | 9 | 1001 |
| p1,p2,p3 | 8 | 1000 |
| p4 | 7 | 0111 |
| p1,p4 | 6 | 0110 |
| p2,p4 | 5 | 0101 |
| p1,p2,p4 | 4 | 0100 |
| p3,p4 | 3 | 0011 |
| p1,p3,p4 | 2 | 0010 |
| p2,p3,p4 | 1 | 0001 |
当然还有种状态是
u.copy(p1="c1",p2="c2",p3="c3",p4="c4"),所有的参数都被重新赋值。那这种状态值根据上面的规律那就是0(0000)。但你反编译代码后会发现并不是这样,编译器会生成直接调用copy函数而不是copy$default函数,因为不需要再判断哪一位参数是否需要赋值了,所以直接调用copy函数
。
kotlin中参数可以任意调整位置比如u.copy(p2="c2",p1="c1"),那这算一种情况吗?
反编译后可以看见它的状态值还是12,编译器会先把他们的顺序调整为参数的顺序。
public static final void main() {
U u = new U("p1", "p2", "p3", "p4");
Object var2 = null;
Object var3 = null;
String var4 = "c1";
String var5 = "c2";
U.copy$default(u, var4, var5, (String)var3, (String)var2, 12, (Object)null);
}