skip to content

How does `init` block timing relate to companion-object initialization and to secondary constructors with `this(...)` delegation?

level: seniorimportance: nice to knowfreq 30%

answer

  1. Companion init: once, at class load, before instances
  2. init blocks: per instance, declaration order
  3. Secondary must delegate via : this(...)
  4. Delegation runs primary+init BEFORE secondary body
  5. Order: companion -> params -> props/init -> secondary body

basics

~20 s

The companion object initializes once when the class is first loaded, before any instance exists. init blocks run for each instance. A secondary constructor first delegates to the primary (running init), then runs its own body.

solid answer

~40 s

There are three distinct phases. (1) **Companion-object / static init**: a `companion object`'s property initializers and `init` run **once**, at class load, before any instance — like JVM static initialization. (2) **Per-instance primary construction**: the primary-constructor parameters bind, then property initializers and `init {}` blocks run top-to-bottom — **every** time you create an instance. (3) **Secondary constructors**: a secondary constructor must delegate to the primary (directly or transitively) via `: this(...)`. That delegation runs the **entire primary sequence — including all `init` blocks — first**, and only then does the secondary constructor's own `{ ... }` body execute. So `init` blocks always run before a secondary constructor's body, and the companion's init has already run long before either.

code

kotlin · 7 lines
kotlin
class Widget(val id: Int) {
    companion object { init { println("loaded once") } }
    init { println("init for id=$id") }
    constructor() : this(0) { println("secondary body") }
}
// first Widget(): "loaded once", "init for id=0", "secondary body"
// second Widget(7): only "init for id=7" (companion already loaded)

go deeper

for a junior

Knows companion init runs once and instance init runs per object.

for a middle

Explains secondary constructors delegate to the primary and that init runs before the secondary body.

for a senior

Lays out the full companion -> primary/init -> secondary-body timeline and places setup logic accordingly.

for a principal

Reasons about class-load timing, singleton/companion side effects, and where to enforce invariants for testability and correctness.

## Three initialization timelines ### 1. Companion object (once, at class load) ```kotlin class Service { companion object { val ID = computeId() // runs once, at class load init { log("class loaded") } } } ``` A `companion object` is a singleton tied to the class. Its initializers and `init` block run **once**, when the class is first referenced/loaded — analogous to a Java `static {}` block. This happens **before any instance** is created and is **not** repeated per instance. ### 2. Per-instance primary construction (every instance) When you call a constructor that resolves to the primary, parameters bind, then the property-initializer/`init` sequence runs top-to-bottom (see the ordering rule). This repeats for **every** instance. ### 3. Secondary constructors and `this(...)` delegation Kotlin requires every **secondary constructor** to delegate to the primary constructor, directly or through another secondary, using `: this(...)`: ```kotlin class Box(val w: Int, val h: Int) { init { println("primary/init, area=${w * h}") } constructor(side: Int) : this(side, side) { println("secondary body") } } // Box(5) prints: "primary/init, area=25" THEN "secondary body" ``` The delegation `this(side, side)` runs the **whole primary sequence — all property initializers and all `init` blocks — first**. Only after that does the secondary constructor's body run. So **`init` always precedes a secondary body**. ## Ordering summary 1. (Once) companion-object initializers + companion `init`. 2. (Per instance) primary-constructor params bind. 3. (Per instance) property initializers + `init {}` blocks, in declaration order. 4. (Per instance, if via a secondary ctor) the secondary constructor body. ## Why it matters - Put **shared, instance-independent** setup (constants, registries) in the companion. - Put **per-instance invariants** in `init`. - Don't put critical validation only in a secondary body — it won't run for instances created via the primary, and it runs **after** `init` anyway.

  • Does the companion init run again for the second instance?
    No. It runs exactly once at class load; only the per-instance init sequence repeats.
  • Can a secondary constructor's body run before the init blocks?
    No. Delegation to the primary runs all init blocks first; the secondary body runs afterward.

Companion init is opening the shop once in the morning; each customer (instance) triggers the per-order init; a special-order form (secondary ctor) is processed only after the standard order (primary/init) is filled.

saying these in an interview costs you the question

  • Saying companion init runs per instance
  • Thinking a secondary body runs before init blocks
  • Believing a secondary constructor can skip primary delegation
  • Putting per-instance validation only in a secondary body
  • Confusing companion init order with instance init order

context