original source : https://medium.com/keepsafe-engineering/an-in-depth-look-at-kotlins-initializers-a0420fcbf546


Constructors vs Initializers

One aspect of class creation in Kotlin that might surprise people coming from other languages is that, when writing a class, there a few places that you can define code that will be executed when you create an instance of the class:

Property initializers

When you declare a property, you can immediately assign it a value like this:

val count: Int = 0

You’ll frequently use constant values as initializers, but you can also call a function or use a property delegate in order to run arbitrary code.

Initializer blocks

If you want more complex code, you can also use an initializer block, defined with the init keyword:

init {
   /* do some setup here */

You can define those blocks anywhere at the top level of a class declaration, and they will be executed as part of class construction. You can even define more than one initializer block if you want, and they will all be executed.


Kotlin also has constructors, which can be defined in the class header or in the body of the class definition. You can define multiple secondary constructors, but only one will be called when you create a class instance unless the constructor explicitly calls another one. Constructors can also have default argument values which are evaluated each time the constructor is called. Like property initializers, these can be function calls or other expressions that will run arbitrary code.

Execution order

All of those features are great, but it can be easy to overlook how they all interact, especially when inheritance is involved. So what order will code run if you define a class with all of them?

First, default constructor arguments are evaluated, starting with argument to the constructor you call directly, followed by arguments to any delegated constructors. Next, initializers (property initializers and init blocks) are executed in the order that they are defined in the class, top-to-bottom. Finally, constructors are executed, starting with the primary constructor and moving outward through delegated constructors until the constructor that you called is executed. The constructor order is probably the most surprising, since no matter where in the class the constructor is defined, it is always executed after all initializers have run.

That’s a lot of rules, so let’s create an small program to help us visualize all of this:

open class Parent {
    private val a = println("Parent.a")

    constructor(arg: Unit=println("Parent primary constructor default argument")) {
        println("Parent primary constructor")

    init {

    private val b = println("Parent.b")

class Child : Parent {
    val a = println("Child.a")

    init {
        println("Child.init 1")

    constructor(arg: Unit=println("Child primary constructor default argument")) : super() {
        println("Child primary constructor")

    val b = println("Child.b")

    constructor(arg: Int, arg2:Unit= println("Child secondary constructor default argument")): this() {
        println("Child secondary constructor")

    init {
        println("Child.init 2")

If we construct an instance of Child by calling its secondary constructor with Child(1), what will be printed to the console?

Here’s the output:

Child secondary constructor default argument
Child primary constructor default argument
Parent primary constructor default argument
Parent primary constructor
Child.init 1
Child.init 2
Child primary constructor
Child secondary constructor

As you can see, initializers are run top to bottom at the beginning of a class’ primary constructor. If you call a secondary constructor, the constructor that gets delegated to will be run before the secondary constructor. And most importantly, superclasses will be fully constructed before any subclass constructors will be run.

Comments are closed.

Post Navigation