Explain property initialization order and how Kotlin properties surface to Java callers (including the `@JvmField` escape hatch).
answer
- init blocks + initializers run top-to-bottom, after ctor params
- reading a later property gives its default value
- Java sees getX()/setX(); isFoo keeps is-name
- @JvmField = public field, no accessors, no custom getter/open/const/lateinit
- const val = static final inlined; @JvmStatic for companion accessors
basics
~20 sProperties and init blocks run top to bottom in the order they appear, after the primary constructor's parameters are set. From Java, Kotlin properties look like getX()/setX() methods unless you annotate the field with @JvmField to expose it directly.
solid answer
~40 sInitializers and `init` blocks execute **in source order**, after the primary-constructor parameter properties are assigned. So a property initializer can use earlier properties but referencing a later one yields its default/`null` (a real footgun with custom accessors or open classes). On the JVM, each Kotlin property becomes a private backing field plus `getX()`/`setX()` accessors; `is`-prefixed `Boolean` properties keep the `is` name (`isReady()`/`setReady(...)`). `@JvmField` removes the accessors and exposes the field directly as a public Java field — valid only on properties with a backing field, no custom accessors, non-`open`, and not `lateinit`/`const`/`override`. For `companion object` constants use `const val` (compile-time constant inlined as a static final) or `@JvmStatic`/`@JvmField` to flatten access from Java.
code
kotlin · 8 linesclass Order(val qty: Int) {
val subtotal = qty * price // BUG: price read before it's set -> 0
val price = 10
}
// Fix: declare price before subtotal, so init order assigns it first.
class Vec(@JvmField var x: Double, @JvmField var y: Double)
// Java: vec.x = 1.0; (direct field, no getX())go deeper
Knows initializers run top to bottom and that Java sees getX()/setX().
Can explain the forward-reference default-value pitfall and the is-prefixed boolean naming.
Applies @JvmField, const val, @JvmStatic correctly and reasons about init order with open classes.
Designs cross-language API surfaces (serialization, framework reflection, constants) and codifies interop conventions for the team.
## Initialization order For a class with a primary constructor, initialization proceeds in this order: 1. Primary-constructor **parameter properties** (`class C(val a: Int)`) are assigned from the arguments. 2. **Property initializers** and **`init { }` blocks** run **in the order they are written**, interleaved, top to bottom. ```kotlin class C(val a: Int) { val b = a + 1 // (1) runs: a is set init { println(b) } // (2) runs: prints b val c = b + 1 // (3) runs after the init above } ``` Because it's strictly top-down, an earlier initializer that reads a **later** property sees that property's default value (`0`/`false`/`null`) — a classic bug, especially when an `open` property is read from a superclass constructor before the subclass initializer runs. ## How properties appear to Java A Kotlin property with a backing field compiles to: - a **private field**, plus - public **`getX()`** and (for `var`) **`setX(...)`** methods. `Boolean` properties named `isFoo` keep that form: getter `isFoo()`, setter `setFoo(...)`. ```kotlin class User(var name: String, val isAdmin: Boolean) // Java: user.getName(); user.setName("x"); user.isAdmin(); ``` Computed properties (no field) expose only the getter. ## `@JvmField` — expose the field directly `@JvmField` tells the compiler to emit a **public field** and **no accessors**, so Java reads `obj.x` instead of `obj.getX()`: ```kotlin class Point(@JvmField var x: Int, @JvmField var y: Int) // Java: point.x = 5; point.y; ``` Constraints: the property must have a **backing field**, **no custom getter/setter**, must **not** be `open`, `override`, `const`, or `lateinit`, and must not be in an interface. ## Companion constants and Java - `const val MAX = 10` (only for primitives/`String`, top-level or in `object`/`companion object`) becomes a **`public static final`** inlined at the call site — the most Java-friendly constant. - A plain `val` in a `companion object` is accessed from Java as `Outer.Companion.getX()` unless you add `@JvmStatic` (static accessor) or `@JvmField` (static field). ## Why this is on the senior bar Getting init order wrong causes subtle null/zero bugs; getting interop wrong leaks awkward `Companion.getX()` calls or breaks serializers/frameworks that expect plain fields. Knowing `@JvmField`, `const`, and `@JvmStatic` lets you design clean cross-language APIs.
- What value does `subtotal` get in the buggy example above?0 — `price` hasn't been initialized when `subtotal`'s initializer runs, so it reads price's default Int value 0.
- When can't you use `@JvmField`?On properties with custom accessors, `open`/`override`/`const`/`lateinit` properties, or members of an interface — they have no plain public-field representation.
- How do you expose a companion `val` as a simple Java static field?Annotate it `@JvmField` (static field) or, for compile-time constants, use `const val`; `@JvmStatic` instead gives a static accessor method.
saying these in an interview costs you the question
- Claiming init blocks run before constructor parameters are set
- Saying source order doesn't matter for initializers
- Thinking `@JvmField` works with custom getters or on `open` properties
- Confusing `const val` with `@JvmStatic`
- Assuming Java always calls `getX()` even with `@JvmField`