skip to content

Explain how Kotlin's primary constructor can declare properties inline, and what `val`/`var` in the constructor header does versus a plain parameter. When do you need an `init` block or a secondary constructor?

level: middleimportance: must knowfreq 80%

answer

  1. val/var in header = property; bare param = constructor-only
  2. init blocks run in declaration order with initializers
  3. secondary constructor must delegate with : this(...)
  4. default args + @JvmOverloads beat overloaded constructors
  5. private constructor + companion factory

basics

~10 s

Putting val or var before a primary-constructor parameter makes it a stored property of the class in one line. Without val/var it's just a constructor argument. init blocks run setup code at construction.

solid answer

~40 s

The primary constructor sits in the class header: `class User(val name: String, var age: Int)`. Prefixing a parameter with `val` (read-only) or `var` (mutable) declares **and** initializes a property in a single line — no separate field + assignment boilerplate. A bare parameter like `class User(name: String)` is constructor-scoped only; it's usable inside `init` blocks and property initializers but is not a member. `init { }` blocks hold construction logic and run **in declaration order, interleaved with property initializers**, as part of the primary constructor. Secondary constructors (`constructor(...)`) provide alternate signatures and must delegate to the primary via `: this(...)`. Default arguments (`age: Int = 0`) plus `@JvmOverloads` often eliminate the need for overloaded secondary constructors entirely.

code

kotlin · 13 lines
kotlin
class User private constructor(
    val id: Long,
    val name: String,
    var active: Boolean = true,   // default arg
) {
    init { require(name.isNotBlank()) { "name required" } }

    companion object {
        fun create(name: String) = User(id = nextId(), name = name)
        private var seq = 0L
        private fun nextId() = ++seq
    }
}

go deeper

for a junior

Knows val/var in the header declares properties and init runs setup.

for a middle

Distinguishes bare params from properties, explains init/initializer ordering, and secondary-constructor delegation.

for a senior

Chooses default args over secondary constructors, uses private constructor + companion factory, and @JvmOverloads for Java interop.

for a principal

Designs construction for invariants and immutability, weighs builder vs. default-arg APIs, and considers binary compatibility of adding parameters.

## The primary constructor lives in the header Kotlin compresses field declaration, constructor parameter, and assignment into the class header: ```kotlin class User(val name: String, var age: Int) ``` This single line is equivalent to a Java class with two fields, a constructor that assigns them, a getter for `name`, and a getter+setter for `age`. ## `val`/`var` vs. a bare parameter - `val name: String` → declares a **read-only property** (getter only) initialized from the argument. - `var age: Int` → declares a **mutable property** (getter + setter). - `name: String` (no keyword) → an ordinary **constructor parameter**. It is visible to `init` blocks and inline property initializers but is **not** a member field; you cannot reference `this.name` after construction. ```kotlin class User(name: String) { // parameter, not a property val displayName = name.uppercase() // used here at construction time } ``` ## `init` blocks The primary constructor has no body, so initialization logic goes in `init { }` blocks. The crucial rule: **init blocks and property initializers run top-to-bottom in the order they appear**, all as part of the primary constructor. ```kotlin class Account(initial: Long) { val opened = System.nanoTime() // 1 init { require(initial >= 0) // 2: validation { "balance must be >= 0" } } var balance = initial // 3 } ``` `require`/`check`/`init` are where you validate invariants. ## Secondary constructors Declared with the `constructor` keyword inside the body. Each **must delegate** to the primary (directly or transitively) using `: this(...)`: ```kotlin class Point(val x: Int, val y: Int) { constructor(v: Int) : this(v, v) // delegates to primary } ``` Init blocks run **before** the secondary constructor's body, because delegation to the primary happens first. ## Prefer default arguments over overloads Kotlin's **default parameter values** usually remove the need for secondary constructors: ```kotlin class Server(val host: String = "localhost", val port: Int = 8080) Server(); Server(port = 9090) // named args pick which to set ``` For Java callers who can't see Kotlin defaults, add `@JvmOverloads` to generate the overload set. ## Visibility on the constructor You can restrict the primary constructor: `class Secret private constructor(...)`, commonly paired with a factory in a `companion object`.

  • In what order do property initializers and init blocks execute?
    Strictly top-to-bottom in source order, interleaved as written, all as part of the primary constructor before any secondary constructor body.
  • How can a Java caller use a Kotlin default-argument constructor?
    Annotate the constructor with `@JvmOverloads` so the compiler emits the full set of overloaded constructors Java can call.

The header val/var is like a form field that both asks for input AND files it permanently; a bare parameter is a question you only use during the interview, then forget.

saying these in an interview costs you the question

  • Saying a bare constructor parameter becomes a field/property
  • Claiming init blocks run after secondary constructor bodies
  • Not knowing secondary constructors must delegate to the primary
  • Forgetting that initializer/init order is source order
  • Reaching for secondary constructors when default args suffice

context