skip to content

How does @JvmStatic behave on a companion-object property, and how does that differ from @JvmField and const?

level: middleimportance: should knowfreq 45%

answer

  1. @JvmStatic property → static getX()/setX()
  2. @JvmField → raw public static field, no accessors
  3. const val → inlined compile-time constant
  4. JvmField blocks custom accessors/open/override/delegate
  5. Can't combine JvmStatic + JvmField on one property

basics

~10 s

@JvmStatic on a property creates static getter/setter methods on the outer class. @JvmField instead exposes the raw field directly. const makes a compile-time constant static final field. They solve different interop needs.

solid answer

~40 s

On a companion property, `@JvmStatic` generates static accessor methods — `getX()`/`setX()` — on the outer class that forward to the companion's accessors, so Java calls `Foo.getX()`. `@JvmField` is different: it removes the accessors and exposes the backing field directly as a public static field, so Java writes `Foo.x` (field access). `const val` applies only to compile-time constants of primitives/String, emits a `static final` field, and inlines the value at Java call sites. So the three are distinct: `@JvmStatic` keeps method-based access (good when you have custom logic, lazy init, or a property delegate); `@JvmField` gives plain field access (no getter overhead, but no encapsulation); `const` is for true constants. You typically choose based on whether you need accessor logic and whether the value is a compile-time constant.

code

kotlin · 7 lines
kotlin
class Settings {
    companion object {
        @JvmStatic var timeout = 30        // Java: Settings.getTimeout()
        @JvmField val LIMIT = 100          // Java: Settings.LIMIT
        const val NAME = "settings"        // Java: Settings.NAME (inlined)
    }
}

go deeper

for a junior

Knows @JvmField gives field access and @JvmStatic gives method access, at a basic level.

for a middle

Clearly separates static accessors vs raw field vs inlined const and picks the right one per scenario.

for a senior

Explains accessor-logic implications (private set, by lazy, validation) and incompatibility constraints.

for a principal

Weighs binary-compatibility and inlining-recompile risks of const versus accessor stability when designing a public interop API.

## Three ways a companion 'static-like' value reaches Java ### 1. `@JvmStatic` property — static accessors ```kotlin class Config { companion object { @JvmStatic var mode: String = "prod" } } // Java: Config.getMode(); Config.setMode("dev"); ``` The compiler generates **static getter/setter methods** on the outer class forwarding to the companion's accessors. The property may have custom `get()/set()`, a delegate (`by lazy`), or validation — all of that runs because access goes through methods. ### 2. `@JvmField` property — direct field ```kotlin class Config { companion object { @JvmField val MAX = 100 } } // Java: int m = Config.MAX; // direct field read ``` `@JvmField` **suppresses accessor generation** and exposes the backing field itself as a `public static` field. Java uses field syntax. Trade-off: no getter/setter logic possible, no `private set`, and it cannot coexist with custom accessors, `open`, `override`, `const`, or delegation. ### 3. `const val` — inlined constant ```kotlin class Config { companion object { const val VERSION = "1.0" } } // Java: String v = Config.VERSION; // value inlined at compile time ``` `const` requires a **compile-time constant** of a primitive type or `String`, emits a `static final` field, and **inlines** the literal into callers. Changing it requires recompiling callers. ## How to choose - Need accessor logic, lazy init, or a delegate, but want `Foo.getX()`? → `@JvmStatic`. - Want zero-ceremony plain field access and no logic? → `@JvmField`. - It is a true compile-time constant? → `const val` (often combined with nothing else; it's already static). ## Gotcha You cannot combine `@JvmStatic` and `@JvmField` on the same property — one wants methods, the other wants a raw field. `const val` is already static, so adding `@JvmStatic` to it is redundant/disallowed.

  • Can a @JvmStatic property still have a private setter?
    Yes — because access goes through generated accessor methods, you can keep a private set; the public static getter is exposed and the setter stays restricted.
  • Why might @JvmField be unsuitable for a lazy-initialized property?
    @JvmField exposes the raw field and forbids delegates, so a by lazy property cannot use it; you'd use @JvmStatic so the lazy accessor logic runs.

saying these in an interview costs you the question

  • Saying @JvmStatic on a property exposes a raw field
  • Claiming @JvmField generates getters/setters
  • Thinking const can be combined freely with @JvmField on the same member
  • Believing const values are read from a field at runtime (they're inlined)
  • Asserting @JvmStatic and @JvmField are interchangeable

context