The problem

What is the order in which Koltin’s companion objects, init blocks, primary constructors, and child constructors are initialized?

I had always thought that the kotlin class’s companion objects and init blocks were executed first, but I did an experiment when I saw that init and class members were executed in order.

According to the official website:

During instance initialization, initialization blocks are executed in the order in which they appear in the class body, interleaved with attribute initializers

class InitOrderDemo(name: String) { val firstProperty = "First property: $name".also(::println) init { println("First initializer block that prints ${name}") } val secondProperty = "Second property: ${name.length}".also(::println) init { println("Second initializer block that prints ${name.length}") } } fun main() { InitOrderDemo("hello")} Output: First Property: Hello First Initializer block that prints hello Second property: 5 Second initializer block that prints 5Copy the code

Init and class members are executed in order, not first. I’m having a problem with that, so let’s do an experiment

The instance test

We implement an abstract class and its subclasses in order of execution:

abstract class Parent(){

    val parent1 = "parent-1".apply {
        println("parent-1 init")
    }

    val parent2 = "parent-2".apply {
        println("parent-2 init")
    }

    constructor(id: Int):this(){
        println("Parent constructor, id:$id")
    }

    init {
        println("Parent init1")
    }

    companion object{

        init {
            println("Parent Comanion init1")
        }

        init {
            println("Parent Comanion init2")
        }

    }

    init {
        println("Parent init2")
    }
}

Copy the code

The subclass:

class Test(id: Int):Parent(id) {

    val test1 = "test-1".apply {
        println("test-1 init")
    }
    val test2 = "test-2".apply {
        println("test-2 init")
    }

    init {
        println("Test init1")
    }

    companion object{

        init {
            println("Test Companion init1")
        }

        init {
            println("Test Companion init2")
        }

    }

    constructor(id: Int, name: String):this(id){
        println("Test sub constructor,id:$id,name:$name")
    }

    init {
        println("Test init2")
    }

}
Copy the code

Call:

Fun main(){println(" create first object ") val ob1 = Test(1) println(" create second object ") val ob2 = Test(2,"Two")}Copy the code

Output result:

Create the first object Parent Comanion init1 Parent Comanion init2 Test Companion init1 Test Companion init2 parent-1 init parent-2 init Parent init1 Parent init2 Parent constructor, Id :1 test-1 init test-2 init test init1 test init2 Create a second object parent-1 init parent-2 init parent init1 parent init2 parent constructor, id:2 test-1 init test-2 init Test init1 Test init2 Test sub constructor,id:2,name:TwoCopy the code

We can’t execute a function directly in a class, but we can execute a function in an init block, which is kotlin’s given syntactic sugar. Take a look at the resulting Java file:

public class Parent { @NotNull private final String parent1; @NotNull private final String parent2; @NotNull public static final Parent.Companion Companion = new Parent.Companion((DefaultConstructorMarker)null); @NotNull public final String getParent1() { return this.parent1; } @NotNull public final String getParent2() { return this.parent2; } public Parent() { String var1 = "parent-1"; String var6 = "parent-1 init"; System.out.println(var6); this.parent1 = var1; var1 = "parent-2"; var6 = "parent-2 init"; System.out.println(var6); this.parent2 = var1; var1 = "Parent init1"; System.out.println(var1); var1 = "Parent init2"; System.out.println(var1); } public Parent(int id) { this(); String var2 = "Parent constructor, id:" + id; boolean var3 = false; System.out.println(var2); } static { String var0 = "Parent Comanion init1"; System.out.println(var0); var0 = "Parent Comanion init2"; System.out.println(var0); } public static final class Companion { private Companion() { } public Companion(DefaultConstructorMarker $constructor_marker) { this(); }}}Copy the code

As you can see from the Decompile code above:

  1. All of theClass member initializationandInit blockBoth are executed when the main constructor initializes
  2. In the associated objectInit blockCorresponding JavaThe static block
  3. Members in the accompanying object are compiled tostatic finaltype
  4. CompanionIs, in fact,Static inner class

About associated objects:

  1. A companion object is much like a static member in Java, and Kotlin has no concept of static
  2. Kotlin can replace static with top-level functions and associated objects
  3. You can use @jVMStatic to generate a truly static member
  4. The private properties of the associated object and the external class can be accessed by each other

Summary: Correct initialization order:

If the class is instantiated for the first time:

Create an object (primary constructor) -> Associated object of the parent class -> Associated object of the child class -> Init blocks of the associated object of the parent class > Objects and init blocks of the parent class are executed in order -> Subclass child constructors -> Subclass objects and init blocks are executed in order -> Subclass constructors

If the companion object has already been initialized, the second initialization does not execute the Companion block:

Create objects (main constructor) -> Objects and init blocks in the parent class are executed in order -> superclass child constructors -> subclass objects and init blocks are executed in order -> subclass constructors