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?
answer
- val/var in header = property; bare param = constructor-only
- init blocks run in declaration order with initializers
- secondary constructor must delegate with : this(...)
- default args + @JvmOverloads beat overloaded constructors
- private constructor + companion factory
basics
~10 sPutting 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 sThe 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 linesclass 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
Knows val/var in the header declares properties and init runs setup.
Distinguishes bare params from properties, explains init/initializer ordering, and secondary-constructor delegation.
Chooses default args over secondary constructors, uses private constructor + companion factory, and @JvmOverloads for Java interop.
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