Compare @JvmField with const for exposing values to Java. When does each apply, and what does Java see?
answer
- const = compile-time, inlined, primitive/String, val only
- @JvmField = runtime value, any type, val or var
- const @JvmField is illegal (redundant)
- Both kill the getter for Java
- Removing const breaks binary compat (inlined)
basics
~20 sconst makes a compile-time constant Java sees as a static final field. @JvmField exposes a normal mutable-or-not field with no accessors. You cannot combine them — const already produces a public static final field for Java.
solid answer
~40 s`const val` is for **compile-time constants** of primitive or `String` type, known at compile time, declared `top-level`, in an `object`, or in a `companion object`. The compiler inlines its value and exposes it to Java as a `public static final` field — no accessor, no `Companion` hop. `@JvmField` is broader: it works on instance `val`/`var` and companion properties of any type, exposing the backing field directly but **not** inlining the value and **not** requiring a compile-time-constant initializer. You cannot mark a property both `const` and `@JvmField` — `const` already yields a static final field, so `@JvmField` is redundant and disallowed. For a companion `val` that is a runtime value (e.g., `val LOGGER = ...`), `@JvmField` is the tool; for `val MAX = 100`, prefer `const`.
code
kotlin · 8 linesclass Settings {
companion object {
const val VERSION = "2.0" // inlined static final String
@JvmField val BUILT_AT = now() // runtime static final field
}
}
// const val VERSION + @JvmField -> compile error: redundant
fun now() = System.currentTimeMillis()go deeper
Knows const = a constant and @JvmField = a plain field, even if fuzzy on the rules.
States const's type/val restrictions, that it inlines, and that const+@JvmField is illegal.
Explains the inlining/binary-compatibility consequence of const and picks the right tool per scenario.
Reasons about ABI evolution, when inlined constants are appropriate for a published library, and migration risk.
## Two ways to give Java a clean field Both `const` and `@JvmField` remove the getter indirection, but they apply to different cases. ### const val — compile-time constant `const` means the value is known **at compile time** and gets **inlined** into call sites. Restrictions: - only `val` (never `var`), - type must be a **primitive** (`Int`, `Long`, `Boolean`, `Double`, etc.) or `String`, - initializer must be a compile-time constant expression, - must be **top-level**, in an `object`, or in a `companion object` (not on a regular class instance). Java sees a `public static final` field. Because the value is inlined, removing/renaming a `const` is a **binary-incompatible** change for already-compiled Java callers. ```kotlin object Limits { const val MAX = 100 // Java: Limits.MAX -> public static final int } ``` ### @JvmField — expose the backing field `@JvmField` works on **runtime** values and instance properties. It just exposes the backing field (suppressing accessors). The value is read from the field at runtime — **not inlined**. ```kotlin class Service { @JvmField val createdAt = System.currentTimeMillis() // runtime value, instance field } class Reg { companion object { @JvmField val DEFAULT = Config() // Java: Reg.DEFAULT, static field, no Companion hop } } ``` ## Why you can't combine them A `const val` already compiles to a `public static final` field accessible directly from Java. Adding `@JvmField` would be redundant, so the compiler **rejects `const @JvmField`**. ## Choosing | Need | Use | |------|-----| | Primitive/String literal constant, inlined | `const val` | | Runtime-computed value or non-primitive type | `@JvmField val` | | Mutable field for Java | `@JvmField var` (const can't be var) | | Companion constant, no `Companion` indirection | `const` (if literal) else `@JvmField` | ## Java-visible shape - `const val` → `public static final` (inlined). - `@JvmField val` on companion/object/top-level → `public static final` field (value at runtime). - `@JvmField var` instance → `public` mutable field.
- Why is renaming a `const val` risky for Java callers?Its value is inlined into already-compiled Java bytecode; the old class still holds the literal, and recompilation against a renamed const breaks. It's a binary-incompatible change.
- Can you make a companion `var` a const?No — `const` requires `val`. For a mutable Java-visible companion field use `@JvmField var`.
saying these in an interview costs you the question
- Saying you can apply both const and @JvmField together
- Claiming const works for any type (it's primitives + String only)
- Saying @JvmField inlines the value like const
- Thinking const can be a var
- Not knowing const can be inlined and thus binary-fragile