The problem

When using the data class copy function, copy can take various combinations of arguments from the class. How does the compiler implement this? Does it generate a separate copy function for each case?

The source code

data class U(val p1: String,val p2:String,val p3:String,val p4:String)
fun main(a) {
    val u = U("p1"."p2"."p3"."p4")
    val copy = u.copy()
}

Copy the code

decompiling

// Delete some code
public final class T2Kt {
   public static final void main(a) {
      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;
        }

        returnvar0.copy(var1, var2, var3, var4); }}Copy the code

When using copy with no arguments, the compiler automatically generates a binary of 1111 for var5=15, in the copy$default() method with 1(0001), 2(0010), 4(0100), 8(1000) and the result of the operation are not zero, so each parameter value uses the value of the original object, that is, no parameter value changes.

Just type p1: u.copy(p1=”c1″). What is the number generated by the compiler?

14 (1110), and 1 with 0 after operation, var1 is not overwritten by p1 in the original object, but uses the parameter value we passed in. And 2,4,8 and operations are not 0 at all, and the arguments use values from the original object.

What about p1,p2? : u.copy(p1=”c1″,p2=”c2″), what is the number generated by the compiler?

12(1100) and 1,2 are 0 after the operation, so the code looks like p1,p2 uses the parameter values we passed in, and p3 and p4 are overwritten by the original object.

conclusion

So, from the above examples, we can see the rule: four parameters can be represented by four bits, (0,0,0,0) ->(p4,p3,p2,p1). 0 indicates that the positional parameter is entered, and 1 indicates that the original object value is used for no input parameter. But the order of the bits and arguments is reversed, with the first bit representing P4 and the last bit representing P1.

Example:

  • 1111->() : No input
  • 1011->(p3) : enter only P3
  • 0111->(P4) : Enter only P4
  • 0011->(p3,p4) : enter p3,p4

There are 4 arguments in class U, so there are 2^4=16 cases of input arguments. Here’s what it’s all about:

The input parameters The status value binary
There is no 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

There is also u.copy(p1=”c1″,p2=”c2″,p3=”c3″,p4=” C4 “), where all parameters are reassigned. The value of this state according to the rule above is 0 (0000). If you decompile the code, you’ll see that this isn’t the case. Instead of calling copy$default, the compiler will simply call copy because it’s no longer necessary to determine which parameter needs to be assigned.

In kotlin, arguments can be arbitrarily positioned, such as u.copy(p2=”c2″,p1=”c1″). Is this a case? After decompilation, you can see that its state value is still 12, and the compiler will first adjust their order to the order of the arguments.

public static final void main(a) {
   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);
}
Copy the code