In Java, when we define a class, some variables are mandatory and others are optional. For example, let’s create a Person class where name is mandatory and gender sex and isChinese are optional.


One, two, three, four, five, sixCopy the code
public class Person {
   public Person(String name) {}
   public Person(String name, int sex) {}
   public Person(String name, boolean isChinese){}
   public Person(String name, int sex, boolean isChinese) {}
}
Copy the code

This is fine when there are only two optional parameters, but when additional attributes are added, we need to implement more constructor overloading. This makes the problem of Telescoping Constructor more likely in Java, which in turn affects our development efficiency and code readability.

In Kotlin, this problem is well addressed. This is the default argument for the method mentioned, which is actually quite simple and is supported in other languages.

Let’s take a look at what the default argument is. Here is a Book class and its constructor (Kotlin code)


1
2
3
Copy the code
class Book(var name: String, val isChineseBook: Boolean = true,
          val hasMultipleAuthor: Boolean = false, val isPopular: Boolean = false,
           val isForChildren: Boolean = false) {
Copy the code

We can use the following Kotlin code when calling


One, two, three, four, five, sixCopy the code
1 Book("Book0")
2 Book("Book1", isForChildren = false)
3 Book("Book2", true)
4 Book("Book3", true, true)
5 Book("Book4", true, true, true)
6 Book("Book5", true, true, true, true)
Copy the code

Book(“Book1”, isForChildren = false). This is a nice feature that greatly enhances the readability of the code.

However, this feature of Kotlin only applies to scenarios where Kotlin code is called. If it is in Java code, we still have to fill in the full parameters. This is really frustrating. A workaround, however, is to use the @jvMoverloads annotation, as shown below


1
Copy the code
class People @JvmOverloads constructor(val name: String, val sex: Int = 1, val isChinese: Boolean = true)
Copy the code

Invoke the sample effect in Java


1
2
3
4
Copy the code
//call constructor with JVMOverloads
People people = new People("");
People people1 = new People("", 0);
People people2 = new People("", 1, true);
Copy the code

So how does JvmOverloads work?

What @jvMoverloads does is tell the compiler to automatically generate multiple overloads of this method. Because we can verify this by decompiling analysis


12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32Copy the code
@JvmOverloads public People(@NotNull String name, int sex, boolean isChinese) { Intrinsics.checkParameterIsNotNull(name, "name"); super(); this.name = name; this.sex = sex; this.isChinese = isChinese; } // $FF: synthetic method @JvmOverloads public People(String var1, int var2, boolean var3, int var4, DefaultConstructorMarker var5) { if((var4 & 2) ! = 0) { var2 = 1; } if((var4 & 4) ! = 0) { var3 = true; } this(var1, var2, var3); } @JvmOverloads public People(@NotNull String name, int sex) { this(name, sex, false, 4, (DefaultConstructorMarker)null); } @JvmOverloads public People(@NotNull String name) { this(name, 0, false, 6, (DefaultConstructorMarker)null); }Copy the code

Note that the overloaded methods above are not generated as combinations, such as public People(@notnull String name, int sex, Boolean isChinese), because this is also for readability and to avoid potential method signature conflicts.

Finally, let’s look at the implementation of default parameters in Kotlin. Because there are some clever aspects of programming.

Here we’ll use the Book class we just mentioned


1
2
3
4
Copy the code
class Book(var name: String, val isChineseBook: Boolean = true,
          val hasMultipleAuthor: Boolean = false, val isPopular: Boolean = false,
           val isForChildren: Boolean = false) {
}
Copy the code

By decompiling, we get some code that looks like this


12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30Copy the code
public Book(@NotNull String name, boolean isChineseBook, boolean hasMultipleAuthor, boolean isPopular, boolean isForChildren) { Intrinsics.checkParameterIsNotNull(name, "name"); super(); this.name = name; this.isChineseBook = isChineseBook; this.hasMultipleAuthor = hasMultipleAuthor; this.isPopular = isPopular; this.isForChildren = isForChildren; } // $FF: synthetic method public Book(String var1, boolean var2, boolean var3, boolean var4, boolean var5, int var6, DefaultConstructorMarker var7) { if((var6 & 2) ! = 0) { var2 = true; } if((var6 & 4) ! = 0) { var3 = false; } if((var6 & 8) ! = 0) { var4 = false; } if((var6 & 16) ! = 0) { var5 = false; } this(var1, var2, var3, var4, var5); }Copy the code

Isn’t that a little different? It only generates two constructors instead of the so-called multi-parameter combination constructor. And what’s even more interesting is when we call it this way


One, two, three, four, fiveCopy the code
Book("Book0")
Book("Book2", true)
Book("Book3", true, true)
Book("Book4", true, true, true)
Book("Book5", true, true, true, true)
Copy the code

The corresponding bytecode decompiled into Java is


One, two, three, four, fiveCopy the code
new Book("Book0", false, false, false, false, 30, (DefaultConstructorMarker)null);
new Book("Book2", true, false, false, false, 28, (DefaultConstructorMarker)null);
new Book("Book3", true, true, false, false, 24, (DefaultConstructorMarker)null);
new Book("Book4", true, true, true, false, 16, (DefaultConstructorMarker)null);
new Book("Book5", true, true, true, true);
Copy the code

We’ll notice that there are a lot of numbers on it, like 30,14,28,24,16, etc. So how are these numbers generated?

For each constructor parameter,

  • They all have one location, the location where the method is declared, and we’ll use I instead. Notice that I start at zero,
  • Each parameter has a mask value, which is 2 to the power of I. For example, the mask value of the 0th position is 1, the mask value of the first position is 2, and so on.
  • If the compiler detects that some parameters were not called during the call, it sums up the mask values of those parameters to produce the number we mentioned above.

Examples are as follows

For example, Book(” Book0 “) we pass the first argument, so the last 30 is calculated from the mask value of 2 + 4 + 8 + 16 missing positions.

Knowing the rules for generating mask values helps us understand the compiler generated constructor.


12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20Copy the code
// $FF: synthetic method public Book(String var1, boolean var2, boolean var3, boolean var4, boolean var5, int var6, DefaultConstructorMarker var7) { if((var6 & 2) ! = 0) { var2 = true; } if((var6 & 4) ! = 0) { var3 = false; } if((var6 & 8) ! = 0) { var4 = false; } if((var6 & 16) ! = 0) { var5 = false; } this(var1, var2, var3, var4, var5); }Copy the code

This constructor determines whether a value is assigned to a parameter at a location based on the mask, and sets the default value if no value is assigned.

This use of mask or flag is a neat way to reduce the generation of unnecessary overloaded methods. For us to deal with similar problems later, it provides some ideas and reference value.