How does @JvmStatic behave on a companion-object property, and how does that differ from @JvmField and const?
answer
- @JvmStatic property → static getX()/setX()
- @JvmField → raw public static field, no accessors
- const val → inlined compile-time constant
- JvmField blocks custom accessors/open/override/delegate
- 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 sOn 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 linesclass 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
Knows @JvmField gives field access and @JvmStatic gives method access, at a basic level.
Clearly separates static accessors vs raw field vs inlined const and picks the right one per scenario.
Explains accessor-logic implications (private set, by lazy, validation) and incompatibility constraints.
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