In Kotlin, we often see main constructors, member variables, and init code blocks (also called initializers). What are their execution timing and order? Take a look at the official example:
class InitOrderDemo(name: String) { val firstProperty = "First property: $name".also(::println) init { println("First initializer block that prints ${name}") } val secondProperty = "Second property: ${}".also(::println) init { println("Second initializer block that prints ${}") } }
The official example only shows the print results and does not conduct in-depth analysis.
The following analysis is based on the following questions.
Why can member variables directly use constructor parameters?
What is the order of execution of member variables and init initializers?
What is the order of execution of constructors and init initializers?
In the above code, call InitOrderDemo("hello"), and the printing result is as follows:
First property: hello
First initializer block that prints hello
Second property: 5
Second initializer block that prints 5
You can see that the execution order is executed in the order they are declared.
So why is this happening? It will be clear at a glance if you convert this code into Java code.
Steps to convert kotlin into Java code:
Open a Kotlin class -> Click tools->kotlin -> Decompile kotlin to java
The code converted to Java is as follows:
import ; import ; import ; import ; public final class InitOrderDemo { @NotNull private final String firstProperty; @NotNull private final String secondProperty; @NotNull public final String getFirstProperty() { return ; } @NotNull public final String getSecondProperty() { return ; } public InitOrderDemo(@NotNull String name) { (name, "name"); super(); String var2 = "First property: " + name; boolean var3 = false; boolean var4 = false; int var6 = false; boolean var7 = false; (var2); Unit var9 = ; = var2; var2 = "First initializer block that prints " + name; var3 = false; (var2); var2 = "Second property: " + (); var3 = false; var4 = false; var6 = false; var7 = false; (var2); var9 = ; = var2; var2 = "Second initializer block that prints " + (); var3 = false; (var2); } }
As you can see, Kotlin's member variable initialization is placed in the constructor, and the init code block is also "copied" into the constructor, and is "copied" in the declaration order, so they are all part of the constructor. So kotlin is still different from Java. Java member variables take precedence over constructor initialization, and Kotlin is initialized in declaration order.
And you can see that Kotlin declared read-only variables with val only add a final keyword in Java, and the get method is generated without a set method.
Let’s slightly change the above kotlin code, add the default value to the main constructor parameters to see what happens, change val to var variable to see what’s different, and add a secondary constructor to see the code execution order:
class InitOrderDemo(name:String="test") { val firstProperty = "First property: $name".also(::println) init { println("First initializer block that prints ${name}") } var secondProperty = "Second property: ${}".also(::println) init { println("Second initializer block that prints ${}") } constructor(name:String,age:Int):this(name){ println("Sub constructor block that prints age= ${age}") } }
The code converted to Java is as follows:
public final class InitOrderDemo { @NotNull private final String firstProperty; @NotNull private String secondProperty; @NotNull public final String getFirstProperty() { return ; } @NotNull public final String getSecondProperty() { return ; } public final void setSecondProperty(@NotNull String var1) { (var1, "<set-?>"); = var1; } public InitOrderDemo(@NotNull String name) { (name, "name"); super(); String var2 = "First property: " + name; boolean var3 = false; boolean var4 = false; int var6 = false; boolean var7 = false; (var2); Unit var9 = ; = var2; var2 = "First initializer block that prints " + name; var3 = false; (var2); var2 = "Second property: " + (); var3 = false; var4 = false; var6 = false; var7 = false; (var2); var9 = ; = var2; var2 = "Second initializer block that prints " + (); var3 = false; (var2); } // $FF: synthetic method public InitOrderDemo(String var1, int var2, DefaultConstructorMarker var3) { if ((var2 & 1) != 0) { var1 = "test"; } this(var1); } public InitOrderDemo() { this((String)null, 1, (DefaultConstructorMarker)null); } public InitOrderDemo(@NotNull String name, int age) { (name, "name"); this(name); String var3 = "Sub constructor block that prints age= " + age; boolean var4 = false; (var3); } }
It can be seen that three constructors are generated, one with name parameters, one with name and age, and one without argument constructor (with default values). The parameterless constructor is delegated to the parameterless constructor with the default value. Variables declared as var generate both get functions and set functions. The code optimization level in the subconstructor is the lowest and is placed in the last execution.
To summarize:
1. No matter which secondary constructor is called, the main constructor is first executed (init member variables and executing init code blocks), and then the secondary constructor code is executed. Therefore, kotlin stipulates that the secondary constructor must be delegated to the main constructor first.
2. Whoever executes member variable initialization and init code blocks first is in the order of their declaration.
With the above analysis, I believe everyone has a clear understanding of the kotlin constructor, member variables, and init code block execution order and principle.
The author also analyzed their execution order from the perspective of bytecode:
Kotlin bytecode layer explores the execution order of constructors, member variables and init code blocks
This is the article about the execution order of Kotlin constructor, member variables and init code blocks. For more related content of Kotlin constructor, please search for my previous article or continue browsing the related articles below. I hope you will support me in the future!