How do you use @JvmField on companion object properties, and what does it fix for Java callers?
answer
- Companion compiles to static nested Companion class
- Default: Outer.Companion.getX()
- @JvmField lifts field to Outer.X static
- const also gives Outer.X (inlined, literals only)
- @JvmStatic is for companion functions, not properties
basics
~20 sA companion property is normally reached from Java via the Companion holder and a getter. @JvmField on it moves the field up to the outer class as a static field, so Java writes Outer.NAME directly.
solid answer
~40 sA `companion object` compiles to a static nested class named `Companion`, with the companion's properties stored there behind accessors. From Java a plain companion `val NAME` is `Outer.Companion.getNAME()` — verbose. Marking it `@JvmField` lifts the backing field to a `public static` field on the **outer** class, so Java calls `Outer.NAME` directly with no `Companion` and no getter. The same constraints apply (backing field, default accessors, not `const`, not `private`). For a compile-time-constant `val`, prefer `const` (also yields `Outer.NAME` static final). `@JvmField` is the choice when the companion value is computed at runtime or isn't a primitive/String, and works on `var` too. Without either annotation, Java must go through `Outer.Companion`.
code
kotlin · 7 linesclass Db {
companion object {
@JvmField val POOL = createPool() // Java: Db.POOL
val raw = 1 // Java: Db.Companion.getRaw()
}
}
fun createPool() = Any()go deeper
Knows companions need special handling and that @JvmField helps Java reach a constant directly.
Explains the Companion static nested class, the default getter path, and how @JvmField promotes the field.
Chooses const vs @JvmField per value kind and designs a clean Java-facing companion API.
Considers the published Java ABI, discoverability, and consistency of static-member exposure across a library.
## How companions look to Java A `companion object` is a singleton compiled into a **static nested class** called `Companion`. Its members live there. So a Kotlin companion property is, by default, accessed from Java as: ```java MyClass.Companion.getNAME(); // getter on the Companion instance ``` That `Companion.` hop plus the getter is awkward for Java consumers. ## What @JvmField does for a companion property `@JvmField` on a companion property **promotes the backing field to a `public static` field on the enclosing (outer) class** and suppresses the accessor: ```kotlin class Registry { companion object { @JvmField val DEFAULT = Config() // runtime value @JvmField var counter = 0 } } ``` ```java Config d = Registry.DEFAULT; // direct static field, no Companion, no getter Registry.counter = 5; // direct static field write ``` ## Relationship to const For **compile-time constants**, `const val` gives the same `Outer.NAME` static-final shape and additionally inlines the value: ```kotlin companion object { const val VERSION = 3 // Java: MyClass.VERSION (static final, inlined) } ``` So: use `const` for primitive/String literals; use `@JvmField` for **runtime-computed** or **non-primitive** companion values, and for `var`. ## Constraints recap (companion case) - backing field + default accessors (no custom `get()`), - not `private`, not `const` (those are mutually exclusive/redundant), - functions in a companion use `@JvmStatic` instead — `@JvmField` is for properties. ## Why it matters Libraries with Java consumers expose well-known constants/singletons via the companion. Without `@JvmField`/`const`, every Java call drags in `Companion`, hurting ergonomics and discoverability. Choosing the right annotation makes the Java API read like a normal `static` member.
- Without @JvmField or const, how does Java read a companion `val NAME`?`MyClass.Companion.getNAME()` — through the static Companion instance and its generated getter.
- For a companion *function*, what's the equivalent annotation?`@JvmStatic`, which generates a real static method on the outer class. @JvmField only applies to properties/fields.
saying these in an interview costs you the question
- Using @JvmStatic on a companion property to get a field (wrong annotation)
- Thinking companion properties are already plain statics by default
- Forgetting the Companion indirection exists for Java
- Claiming @JvmField can't apply to companion var
- Mixing up that const inlines but @JvmField doesn't