skip to content

How do you use @JvmField on companion object properties, and what does it fix for Java callers?

level: middleimportance: should knowfreq 40%

answer

  1. Companion compiles to static nested Companion class
  2. Default: Outer.Companion.getX()
  3. @JvmField lifts field to Outer.X static
  4. const also gives Outer.X (inlined, literals only)
  5. @JvmStatic is for companion functions, not properties

basics

~20 s

A 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 s

A `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 lines
kotlin
class Db {
    companion object {
        @JvmField val POOL = createPool()   // Java: Db.POOL
        val raw = 1                          // Java: Db.Companion.getRaw()
    }
}
fun createPool() = Any()

go deeper

for a junior

Knows companions need special handling and that @JvmField helps Java reach a constant directly.

for a middle

Explains the Companion static nested class, the default getter path, and how @JvmField promotes the field.

for a senior

Chooses const vs @JvmField per value kind and designs a clean Java-facing companion API.

for a principal

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

context